Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions projects/amdsmi/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ Full documentation for amd_smi_lib is available at [https://rocm.docs.amd.com/pr

### Added

- **Added an experimental, opt-in WSL (WDDM/dxg) GPU backend**.
- Built only with `-DENABLE_WSL_BACKEND=ON` (off by default); native builds and packages are unchanged.
- Reads real GPU telemetry through `librocdxg` (`rocdxg_smi_*` APIs); queries with no WDDM equivalent return `AMDSMI_STATUS_NOT_SUPPORTED`. See [Using AMD SMI under WSL](https://rocm.docs.amd.com/projects/amdsmi/en/latest/how-to/amdsmi-wsl-mode.html).

- **Added NIC processor discovery and information API surface**.
- New C APIs: `amdsmi_get_nic_processor_handles()`, `amdsmi_get_nic_device_bdf()`, `amdsmi_get_nic_fw_info()`, `amdsmi_get_nic_port_statistics()`, and `amdsmi_get_nic_vendor_statistics()`.
- `amdsmi_get_nic_processor_handles()` enumerates NIC processors by socket; the BDF, firmware, and port/vendor statistics getters are reserved and currently return `AMDSMI_STATUS_NOT_YET_IMPLEMENTED`.
Expand Down
6 changes: 5 additions & 1 deletion projects/amdsmi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ include(GNUInstallDirs)
option(BUILD_TESTS "Build test suite" OFF)
option(ENABLE_ASAN_PACKAGING "" OFF)

# WSL/WDDM GPU backend. Default OFF so native builds and CI are unchanged. When
# ON, the WSL intercept hooks and mock backend are compiled
option(ENABLE_WSL_BACKEND "Build the experimental WSL (WDDM/dxg) GPU backend" OFF)

if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
option(ENABLE_ESMI_LIB "Build ESMI Library" ON)
else()
Expand Down Expand Up @@ -204,13 +208,13 @@ generic_package()

# Dependencies
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)

pkg_check_modules(DRM REQUIRED IMPORTED_TARGET libdrm)
pkg_check_modules(DRM_AMDGPU REQUIRED IMPORTED_TARGET libdrm_amdgpu)

# Netlink libraries for network interface functionality (required)
# TheRock RUNTIME_DEPS automatically configure PKG_CONFIG_PATH for bundled sysdeps
find_package(PkgConfig REQUIRED)

# Try to use CMake config files first (bundled sysdeps provide these)
find_package(libnl CONFIG QUIET)
Expand Down
25 changes: 25 additions & 0 deletions projects/amdsmi/amdsmi_cli/amdsmi_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,33 @@
AMD_VENDOR_ID = 4098


def check_wsl_dxg():
"""Returns true if running under WSL2 (not Hyper-V) with /dev/dxg present.

/dev/dxg is created by dxgkrnl for any WDDM GPU, so we additionally require
the WSL2 kernel signature in /proc/version to avoid false-positives on
Hyper-V Linux guests or native hosts where dxgkrnl may be loaded.
Vendor confirmation (AMD 0x1002) is deferred to amdsmi_init(), which calls
into librocdxg and rejects non-AMD adapters.
"""
if not Path("/dev/dxg").exists():
return False
try:
osrelease = Path("/proc/sys/kernel/osrelease").read_text(encoding="ascii").lower()
return "microsoft" in osrelease and "wsl" in osrelease
except OSError:
return False


def check_amdgpu_driver():
"""Returns true if amdgpu is found in the list of initialized modules"""
# WSL2: no native amdgpu module; use /dev/dxg via dxgkrnl instead.
# check_wsl_dxg() requires the WSL2 kernel string, so this is safe for
# Hyper-V guests (which lack "wsl" in osrelease) and bare-metal hosts.
# Vendor confirmation (AMD 0x1002) is done inside librocdxg during amdsmi_init().
if check_wsl_dxg():
return True

amd_gpu_status_file = Path("/sys/module/amdgpu/initstate")
if amd_gpu_status_file.exists():
try:
Expand Down
122 changes: 122 additions & 0 deletions projects/amdsmi/docs/how-to/amdsmi-wsl-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
---
myst:
html_meta:
"description lang=en": "How to build and run AMD SMI under Windows Subsystem for Linux (WSL) using the experimental WSL/WDDM backend."
"keywords": "amd-smi, wsl, wsl2, dxg, dxgkrnl, wddm, d3dkmt, windows subsystem for linux, backend, feature flag"
---

# Using AMD SMI under WSL (experimental)

AMD SMI targets native Linux, where it reads GPU data from the `amdgpu` kernel
driver through DRM ioctls and sysfs. Under Windows Subsystem for Linux 2 (WSL2)
those interfaces do not exist: there is no `/sys/class/drm` GPU tree and no
`/dev/kfd`. The GPU is reached instead through the Windows WDDM display driver
using the D3DKMT interface exposed by the `/dev/dxg` device node.

To support this without maintaining a separate build of the tool, AMD SMI has an
optional **WSL backend**. The public API, the CLI, and the Python bindings are
unchanged. When the backend is active, the affected queries are answered from
the WDDM path instead of DRM/sysfs; everything else behaves as before.

```{note}
The WSL backend is experimental and gated behind a build flag that is **off by
default**. A default AMD SMI build and its packages are byte-for-byte the native
tool. When enabled, the backend reads real GPU telemetry through `librocdxg`
(`rocdxg_smi_*` APIs); queries with no WDDM equivalent return
`AMDSMI_STATUS_NOT_SUPPORTED`.
```

## How it works

AMD SMI keeps a single code path per API. The WSL-versus-native decision is made
**once per process**, during `amdsmi_init()`, not scattered through every
function:

```text
amdsmi_get_gpu_* (one dispatcher, backend-agnostic)
│
├─ native → DRM ioctls / sysfs (default)
└─ WSL → WSLGPUBackend → D3DKMT → /dev/dxg → dxgkrnl → Windows KMD
```

Queries that WDDM cannot serve (for example CPU/HSMP metrics, NIC, xGMI fabric,
ECC, partitions) return `AMDSMI_STATUS_NOT_SUPPORTED` on WSL. This is the same
status the native path returns for unsupported hardware, so existing error
handling continues to work.

## Prerequisites

- Windows 11 (or Windows 10 with WSL2) with a WSL-capable AMD GPU driver.
- A WSL2 Ubuntu distribution.
- The `dxgkrnl` module present in the guest (verify with
`ls /sys/module/dxgkrnl`).
- `librocdxg.so.1` installed and resolvable via `dlopen` (the WSL backend
loads it at `amdsmi_init()`; without it, calls return
`AMDSMI_STATUS_DRIVER_NOT_LOADED`). It ships with the ROCm-on-WSL driver
package, not a standard Linux ROCm devel package — verify with
`ldconfig -p | grep librocdxg`.
- `libdxcore.so`, provided by the WSL installation
(`/usr/lib/wsl/lib/libdxcore.so`) — a transitive dependency of
`librocdxg`, not something you need to install directly.

## Building with the WSL backend

The backend is compiled in only when you enable the CMake option. It is off by
default:

```bash
cmake -S . -B build -DENABLE_WSL_BACKEND=ON
cmake --build build --target amd_smi -j"$(nproc)"
```

A build without `-DENABLE_WSL_BACKEND=ON` produces the standard native library;
the WSL source is not compiled and the intercept hooks expand to nothing.

Building with the flag on also requires `hsakmt/rocdxg_smi.h` at compile time,
from a `rocr-runtime/libhsakmt` tree built with its `WIN_SDK`/dxg path —
distinct from the runtime `librocdxg.so.1` dependency above. CMake fails with
a `FATAL_ERROR` if that header is not found alongside the checkout.

## Impact on existing scripts

The WSL backend is designed to be a drop-in. For users with existing automation:

- **No API, CLI, or output-schema changes.** Function signatures, CLI
subcommands, flags, and JSON field names are identical. Scripts that parse
`amd-smi --json` keep working.
- **Some fields report `N/A` / `NOT_SUPPORTED` under WSL.** Where WDDM has no
equivalent (CPU, NIC, xGMI, ECC, partitions), the field is unavailable rather
than wrong. Scripts should already tolerate `N/A` for unsupported hardware;
the same handling covers WSL.
- **Native installs are unaffected.** If you never build with
`-DENABLE_WSL_BACKEND=ON`, nothing changes.

## Verifying

```bash
# Confirm you are on WSL
ls /dev/dxg && echo "wsl present"

ls /sys/module/dxgkrnl && echo "dxgkrnl present"

# Confirm amd-smi is answering through the WSL backend
amd-smi static --asic
```

If the backend is active you will see WDDM-sourced values for the supported
fields and `N/A` for the rest.

## Limitations

- Experimental: the supported-query set is a subset of the native API and will
grow as `librocdxg` exposes more telemetry.
- CPU/ESMI, NIC, switch, fabric, ECC, and partition features are not available
under WSL; those queries return `AMDSMI_STATUS_NOT_SUPPORTED`.
- PCIe info, VRAM type, subvendor/subsystem IDs, and full VBIOS fields depend on
the installed `librocdxg` version and may show `N/A` on older drivers.
- **Per-process GPU usage (`amd-smi process`) is not available under WSL** and
returns `AMDSMI_STATUS_NOT_SUPPORTED`. The WSL process-enumeration ioctl
reports WSL-guest (Linux) PIDs, but the per-process VRAM query expects a
real Windows process handle, not a bare PID — this mismatch is unverified
against hardware, so the query is disabled rather than risk reporting
incorrect per-process memory usage.
3 changes: 3 additions & 0 deletions projects/amdsmi/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ This AMD SMI project supports Linux bare metal and Linux virtual machine guest
environments. For documentation regarding virtualization on SR-IOV Linux hosts,
refer to the [AMD SMI for Virtualization
documentation](https://instinct.docs.amd.com/projects/amd-smi-virt/en/latest/).
Experimental WSL2 support is documented in [Using AMD SMI under
WSL](./how-to/amdsmi-wsl-mode.md).
```

```{note}
Expand All @@ -42,6 +44,7 @@ AMD SMI is the successor to [ROCm SMI](https://github.com/ROCm/rocm-systems/tree
* [CLI tool usage](./how-to/amdsmi-cli-tool.md)
* [AI NIC integration](./how-to/amdsmi-nic-integration.md)
* [Use AMD SMI in a Docker container](./how-to/setup-docker-container.md)
* [Using AMD SMI under WSL (experimental)](./how-to/amdsmi-wsl-mode.md)
:::

:::{grid-item-card} Reference
Expand Down
2 changes: 2 additions & 0 deletions projects/amdsmi/docs/sphinx/_toc.yml.in
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ subtrees:
title: AI NIC integration
- file: how-to/setup-docker-container.md
title: Use AMD SMI in a Docker container
- file: how-to/amdsmi-wsl-mode.md
title: Using AMD SMI under WSL (experimental)

- caption: Reference
entries:
Expand Down
108 changes: 108 additions & 0 deletions projects/amdsmi/include/amd_smi/impl/amd_smi_gpu_backend.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#ifndef AMD_SMI_INCLUDE_IMPL_AMD_SMI_GPU_BACKEND_H_
#define AMD_SMI_INCLUDE_IMPL_AMD_SMI_GPU_BACKEND_H_

#include <vector>

#include "amd_smi/amdsmi.h"

namespace amd::smi {

// Per-GPU backend abstraction. amd_smi.cc dispatches to this instead of
// branching on platform identity. Non-pure virtuals return NOT_SUPPORTED so
// each backend only overrides what it actually implements.
class IGPUBackend {
public:
virtual ~IGPUBackend() = default;

virtual amdsmi_status_t GetAsicInfo(amdsmi_asic_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetBoardInfo(amdsmi_board_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetKfdInfo(amdsmi_kfd_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetVramInfo(amdsmi_vram_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetMemoryTotal(amdsmi_memory_type_t, uint64_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetMemoryUsage(amdsmi_memory_type_t, uint64_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetTempMetric(amdsmi_temperature_type_t, amdsmi_temperature_metric_t,
int64_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetVoltMetric(amdsmi_voltage_type_t, amdsmi_voltage_metric_t, int64_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetPowerInfo(amdsmi_power_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetGpuActivity(amdsmi_engine_usage_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetBusyPercent(uint32_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetClockInfo(amdsmi_clk_type_t, amdsmi_clk_info_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetPcieInfo(amdsmi_pcie_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetDriverInfo(amdsmi_driver_info_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetVbiosInfo(amdsmi_vbios_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetUuid(unsigned int*, char*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetGpuCacheInfo(amdsmi_gpu_cache_info_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetFwInfo(amdsmi_fw_info_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetGpuMetricsInfo(amdsmi_gpu_metrics_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetPowerCapInfo(amdsmi_power_cap_info_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetFanRpms(uint32_t, int64_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetFanSpeed(uint32_t, int64_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t GetFanSpeedMax(uint32_t, uint64_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetVcnBusyPercent(uint32_t*) { return AMDSMI_STATUS_NOT_SUPPORTED; }
virtual amdsmi_status_t AllocFabricTelemetry(uint32_t, amdsmi_fabric_telemetry_t**) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetFabricTelemetryData(amdsmi_fabric_telemetry_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t FreeFabricTelemetry(amdsmi_fabric_telemetry_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}
virtual amdsmi_status_t GetGpuFabricInfo(amdsmi_fabric_info_t*) {
return AMDSMI_STATUS_NOT_SUPPORTED;
}

IGPUBackend(const IGPUBackend&) = delete;
IGPUBackend& operator=(const IGPUBackend&) = delete;

protected:
IGPUBackend() = default;
};

} // namespace amd::smi

#endif // AMD_SMI_INCLUDE_IMPL_AMD_SMI_GPU_BACKEND_H_
6 changes: 6 additions & 0 deletions projects/amdsmi/include/amd_smi/impl/amd_smi_gpu_device.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

#include "amd_smi/amdsmi.h"
#include "amd_smi/impl/amd_smi_drm.h"
#include "amd_smi/impl/amd_smi_gpu_backend.h"
#include "amd_smi/impl/amd_smi_processor.h"

// Forward declaration of UALoE handle type to keep ualoe_lib/ualoe_lib.h out
Expand Down Expand Up @@ -137,6 +138,10 @@ class AMDSmiGPUDevice : public AMDSmiProcessor {
AMDSmiGPUDevice(uint32_t gpu_id, AMDSmiDrm& drm);
~AMDSmiGPUDevice();

// Non-null only on non-Linux backends (e.g. WSL). amd_smi.cc checks this
// before falling through to the Linux rsmi/libdrm path.
IGPUBackend* backend() const { return backend_; }
void set_backend(IGPUBackend* b) { backend_ = b; }
amdsmi_status_t get_drm_data();
pthread_mutex_t* get_mutex();
uint32_t get_gpu_id() const;
Expand Down Expand Up @@ -175,6 +180,7 @@ class AMDSmiGPUDevice : public AMDSmiProcessor {
bool device_has_ualink() const;

private:
IGPUBackend* backend_ = nullptr;
uint32_t gpu_id_;
std::string path_;
amdsmi_bdf_t bdf_;
Expand Down
Loading
Loading