Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
116 changes: 116 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,116 @@
---
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.
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