ROCm-on-WSL: support discrete Radeon (RDNA 3/4) in WSL, not just Strix Halo (#6915)
* WSL ROCm: generalize ROCm-on-WSL bootstrap from Strix-only to any RDNA arch install_rocm_wsl_strixhalo.sh hardcoded gfx1151, so its verify step died on discrete Radeon cards even though the ROCm + librocdxg setup is arch-agnostic. Auto-detect the GPU arch from rocminfo (override via UNSLOTH_WSL_GFX), verify any GPU agent enumerates over DXG, and map the arch to AMD's per-arch wheel family for the optional smoke test (injecting librocdxg into torch/lib so torch's bundled ROCr finds the DXG bridge). Verified on gfx1200 (Radeon RX 9060 XT) in WSL2 + Ubuntu 24.04 -- torch.cuda now enumerates the GPU. * WSL ROCm: trigger the ROCm-on-WSL bootstrap for discrete Radeon GPUs too _maybe_bootstrap_rocm_wsl only fired for Strix APUs (matched via /proc/cpuinfo, which discrete cards don't appear in). Add _wsl_amd_gpu_name() -- queries the Windows host via WMI -- and broaden the trigger gate plus the 'already-usable ROCm' rocminfo check from gfx1151-only to any real GPU agent (gfxNNNN, excluding the gfx11-generic fallback ISA). The generalized bootstrap then auto-detects the arch. Enables 'curl install.sh | sh' to set up ROCm-on-WSL on discrete Radeon RX 7000/9000 in WSL2 + Ubuntu 24.04, not just Strix Halo/Point. * WSL ROCm: address review -- filter generic ISA in bootstrap, bound the host GPU query - install_rocm_wsl_strixhalo.sh: exclude the gfx11-generic fallback ISA in arch detection (grep -v generic), matching install.sh's rocminfo check, so a generic agent listed before the real one can't be picked as the arch. - install.sh: wrap the powershell.exe Win32_VideoController query in _run_bounded (10s timeout) so an unstable WSL-interop / busy host can't hang the installer. * WSL ROCm: harden arch-detect + librocdxg copy under set -eo pipefail (review) - _detected_gfx: append '|| true' so a no-GPU rocminfo (empty pipeline, non-zero under pipefail) doesn't abort the assignment before the '[ -z ]' branch prints the diagnostic + die message. - smoke-test librocdxg copy: gate on '[ -d "$_tlib" ]' instead of '[ -n ]' so a non-directory value can't make cp rename librocdxg to 'lib'. * WSL ROCm: address Codex review (gfx000, 24.04 reroute for discrete, test locator) - Exclude gfx000 (the CPU agent) from the WSL 'usable ROCm' check and the bootstrap arch-detect: match gfx[1-9] (nonzero arch), so a partial ROCm install that only reports the CPU ISA no longer short-circuits the librocdxg setup. (P2) - Reuse the Ubuntu-24.04 reroute for discrete Radeon: broaden _maybe_reroute_strixhalo_to_2404's gate with the same _wsl_amd_gpu_name (WMI) fallback, so a discrete card on 26.04 reroutes to a 24.04 distro like Strix does instead of falling to CPU. Moved _wsl_amd_gpu_name above the reroute and made it self-contained + 10s-bounded (it runs before _run_bounded is defined). (P2) - Update TestInstallShDropinPersistence to locate the gate by its unique '!/generic/' clause now that the gfx1151 literal is gone. (P1) * Condense ROCm-on-WSL comments in install.sh and bootstrap helper * Guard WSL reroute from NVIDIA hybrid hosts and fix GFX-override pipefail check * Honor CUDA_VISIBLE_DEVICES-hidden NVIDIA in the WSL reroute guard * Reuse _has_usable_nvidia_gpu in the WSL reroute guard --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
parent
69f8e0b228
commit
296cacb5a1
3 changed files with 182 additions and 85 deletions
162
install.sh
162
install.sh
|
|
@ -1483,6 +1483,81 @@ elif [ "$OS" = "macos" ]; then
|
|||
fi
|
||||
tauri_diag_marker "$_TAURI_INITIAL_GPU_BRANCH" "none"
|
||||
|
||||
# AMD GPU name from the Windows host via WMI, or empty. Discrete cards aren't in
|
||||
# /proc/cpuinfo, so ask Windows. Cached ("-" = negative), self-contained, bounded
|
||||
# to 10s. Defined here so the reroute below can use it before _run_bounded exists.
|
||||
_WSL_AMD_GPU_NAME_CACHE=""
|
||||
_wsl_amd_gpu_name() {
|
||||
if [ -n "$_WSL_AMD_GPU_NAME_CACHE" ]; then
|
||||
[ "$_WSL_AMD_GPU_NAME_CACHE" = "-" ] && return 1
|
||||
printf '%s' "$_WSL_AMD_GPU_NAME_CACHE"; return 0
|
||||
fi
|
||||
command -v powershell.exe >/dev/null 2>&1 || { _WSL_AMD_GPU_NAME_CACHE="-"; return 1; }
|
||||
_wag_ps="(Get-CimInstance Win32_VideoController | Where-Object { \$_.Name -match 'AMD|Radeon' } | Select-Object -First 1).Name"
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
_wag_n="$(timeout 10 powershell.exe -NoProfile -Command "$_wag_ps" 2>/dev/null | tr -d '\r\n\000')"
|
||||
else
|
||||
_wag_n="$(powershell.exe -NoProfile -Command "$_wag_ps" 2>/dev/null | tr -d '\r\n\000')"
|
||||
fi
|
||||
if [ -n "$_wag_n" ]; then _WSL_AMD_GPU_NAME_CACHE="$_wag_n"; printf '%s' "$_wag_n"; return 0; fi
|
||||
_WSL_AMD_GPU_NAME_CACHE="-"; return 1
|
||||
}
|
||||
|
||||
# ── Bounded command runner ──
|
||||
# Runs a command under a 10s timeout when the `timeout` binary is available,
|
||||
# otherwise runs it unbounded. Keeps a wedged nvidia-smi (blocking during
|
||||
# driver init or after a reset) from hanging the installer: a timed-out probe
|
||||
# exits nonzero and is treated exactly like a failed probe. No-op semantics on
|
||||
# hosts without `timeout` (e.g. macOS) or when the probe is healthy.
|
||||
_run_bounded() {
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Returns 0 (true) when CUDA_VISIBLE_DEVICES is set to "" or "-1", i.e. every
|
||||
# NVIDIA device is deliberately hidden (mixed AMD+NVIDIA hosts steering work to
|
||||
# the AMD card). Unset means all devices visible. nvidia-smi ignores this env
|
||||
# var, so the probes below cannot see the distinction on their own.
|
||||
_cvd_hides_nvidia() {
|
||||
[ "${CUDA_VISIBLE_DEVICES+set}" = "set" ] || return 1
|
||||
_cvd_trim=$(printf '%s' "$CUDA_VISIBLE_DEVICES" | tr -d '[:space:]')
|
||||
[ -z "$_cvd_trim" ] || [ "$_cvd_trim" = "-1" ]
|
||||
}
|
||||
|
||||
# ── NVIDIA usable-GPU helper ──
|
||||
# Returns 0 (true) if an NVIDIA GPU is present and usable.
|
||||
# Primary probe: nvidia-smi -L. Fallback: /proc/driver/nvidia/gpus/ sysfs,
|
||||
# which the NVIDIA driver populates on Linux regardless of nvidia-smi state
|
||||
# -- handles PATH gaps, subprocess timeouts, and driver init races that
|
||||
# could otherwise cause nvidia-smi to fail and silence NVIDIA detection.
|
||||
# A GPU hidden via CUDA_VISIBLE_DEVICES=""/-1 counts as NOT usable (matches
|
||||
# install_llama_prebuilt.py has_usable_nvidia), so AMD/CPU routing still runs.
|
||||
_has_usable_nvidia_gpu() {
|
||||
if _cvd_hides_nvidia; then
|
||||
return 1
|
||||
fi
|
||||
_nvsmi=""
|
||||
if command -v nvidia-smi >/dev/null 2>&1; then
|
||||
_nvsmi="nvidia-smi"
|
||||
elif [ -x "/usr/bin/nvidia-smi" ]; then
|
||||
_nvsmi="/usr/bin/nvidia-smi"
|
||||
fi
|
||||
if [ -n "$_nvsmi" ]; then
|
||||
if _run_bounded "$_nvsmi" -L 2>/dev/null | awk '/^GPU[[:space:]]+[0-9]+:/{found=1} END{exit !found}'; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# Fallback: NVIDIA driver exposes one subdir per GPU under this path.
|
||||
if [ -d /proc/driver/nvidia/gpus ] && \
|
||||
[ -n "$(ls -A /proc/driver/nvidia/gpus 2>/dev/null)" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# Strix Halo ROCm-on-WSL only targets Ubuntu 24.04. On a newer distro (e.g. 26.04)
|
||||
# with a 24.04 distro present, re-run the install there and stop; else fall through
|
||||
# to CPU + the `wsl --install` hint below (never auto-create a distro). Runs before
|
||||
|
|
@ -1493,7 +1568,15 @@ _maybe_reroute_strixhalo_to_2404() {
|
|||
[ "${UNSLOTH_SKIP_ROCM_WSL_SETUP:-0}" = "1" ] && return 0
|
||||
[ "${UNSLOTH_WSL_REROUTED:-0}" = "1" ] && return 0
|
||||
[ -e /dev/dxg ] || return 0
|
||||
grep -qiE 'Ryzen AI Max|Radeon 80[0-9]0S|Strix Halo' /proc/cpuinfo 2>/dev/null || return 0
|
||||
# A usable NVIDIA GPU (common on hybrid AMD+NVIDIA hosts) means the CUDA path works on
|
||||
# this distro, so don't reroute for AMD. _has_usable_nvidia_gpu (moved above) honors
|
||||
# CUDA_VISIBLE_DEVICES=""/-1 and the /proc/driver/nvidia fallback for PATH/timeout gaps.
|
||||
if _has_usable_nvidia_gpu; then return 0; fi
|
||||
# Strix APUs show in /proc/cpuinfo; discrete cards don't, so also try WMI. Either reroutes.
|
||||
if ! grep -qiE 'Ryzen AI Max|Radeon 80[0-9]0S|Strix Halo' /proc/cpuinfo 2>/dev/null \
|
||||
&& ! _wsl_amd_gpu_name >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
# Already ROCm-on-WSL? leave a working GPU alone, whatever the version.
|
||||
if [ -e /opt/rocm/lib/librocdxg.so ] || [ -e /opt/rocm/lib64/librocdxg.so ]; then
|
||||
return 0
|
||||
|
|
@ -1959,61 +2042,6 @@ _has_amd_rocm_gpu() {
|
|||
return 1
|
||||
}
|
||||
|
||||
# ── Bounded command runner ──
|
||||
# Runs a command under a 10s timeout when the `timeout` binary is available,
|
||||
# otherwise runs it unbounded. Keeps a wedged nvidia-smi (blocking during
|
||||
# driver init or after a reset) from hanging the installer: a timed-out probe
|
||||
# exits nonzero and is treated exactly like a failed probe. No-op semantics on
|
||||
# hosts without `timeout` (e.g. macOS) or when the probe is healthy.
|
||||
_run_bounded() {
|
||||
if command -v timeout >/dev/null 2>&1; then
|
||||
timeout 10 "$@"
|
||||
else
|
||||
"$@"
|
||||
fi
|
||||
}
|
||||
|
||||
# Returns 0 (true) when CUDA_VISIBLE_DEVICES is set to "" or "-1", i.e. every
|
||||
# NVIDIA device is deliberately hidden (mixed AMD+NVIDIA hosts steering work to
|
||||
# the AMD card). Unset means all devices visible. nvidia-smi ignores this env
|
||||
# var, so the probes below cannot see the distinction on their own.
|
||||
_cvd_hides_nvidia() {
|
||||
[ "${CUDA_VISIBLE_DEVICES+set}" = "set" ] || return 1
|
||||
_cvd_trim=$(printf '%s' "$CUDA_VISIBLE_DEVICES" | tr -d '[:space:]')
|
||||
[ -z "$_cvd_trim" ] || [ "$_cvd_trim" = "-1" ]
|
||||
}
|
||||
|
||||
# ── NVIDIA usable-GPU helper ──
|
||||
# Returns 0 (true) if an NVIDIA GPU is present and usable.
|
||||
# Primary probe: nvidia-smi -L. Fallback: /proc/driver/nvidia/gpus/ sysfs,
|
||||
# which the NVIDIA driver populates on Linux regardless of nvidia-smi state
|
||||
# -- handles PATH gaps, subprocess timeouts, and driver init races that
|
||||
# could otherwise cause nvidia-smi to fail and silence NVIDIA detection.
|
||||
# A GPU hidden via CUDA_VISIBLE_DEVICES=""/-1 counts as NOT usable (matches
|
||||
# install_llama_prebuilt.py has_usable_nvidia), so AMD/CPU routing still runs.
|
||||
_has_usable_nvidia_gpu() {
|
||||
if _cvd_hides_nvidia; then
|
||||
return 1
|
||||
fi
|
||||
_nvsmi=""
|
||||
if command -v nvidia-smi >/dev/null 2>&1; then
|
||||
_nvsmi="nvidia-smi"
|
||||
elif [ -x "/usr/bin/nvidia-smi" ]; then
|
||||
_nvsmi="/usr/bin/nvidia-smi"
|
||||
fi
|
||||
if [ -n "$_nvsmi" ]; then
|
||||
if _run_bounded "$_nvsmi" -L 2>/dev/null | awk '/^GPU[[:space:]]+[0-9]+:/{found=1} END{exit !found}'; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
# Fallback: NVIDIA driver exposes one subdir per GPU under this path.
|
||||
if [ -d /proc/driver/nvidia/gpus ] && \
|
||||
[ -n "$(ls -A /proc/driver/nvidia/gpus 2>/dev/null)" ]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
# ── Detect GPU and choose PyTorch index URL ──
|
||||
# Mirrors Get-TorchIndexUrl in install.ps1.
|
||||
# On CPU-only machines this returns the cpu index, avoiding the solver
|
||||
|
|
@ -2327,19 +2355,19 @@ _persist_rocm_wsl_dropin() {
|
|||
fi
|
||||
}
|
||||
|
||||
# _wsl_amd_gpu_name is defined earlier so both the reroute and this bootstrap can use it.
|
||||
_maybe_bootstrap_rocm_wsl() {
|
||||
[ "${OS:-}" = "wsl" ] || return 0
|
||||
[ "${SKIP_TORCH:-false}" = "false" ] || return 0
|
||||
[ "${UNSLOTH_SKIP_ROCM_WSL_SETUP:-0}" = "1" ] && return 0
|
||||
# Leave any already-usable GPU completely alone (NVIDIA, or working ROCm).
|
||||
if _has_usable_nvidia_gpu; then return 0; fi
|
||||
# "Usable ROCm" here = rocminfo enumerates the gfx1151 agent. Don't use the
|
||||
# generic _has_amd_rocm_gpu: its broad gfx match accepts "gfx11-generic" and
|
||||
# would skip this bootstrap while the real GPU is still unusable. awk consumes
|
||||
# all input, so rocminfo isn't SIGPIPE'd like `grep -q` would under pipefail.
|
||||
# Usable ROCm = rocminfo enumerates a real GPU agent: gfx[1-9] (excludes gfx000,
|
||||
# the CPU agent) and not the "gfx11-generic" fallback. awk consumes all input so
|
||||
# rocminfo isn't SIGPIPE'd like `grep -q` under pipefail.
|
||||
_ensure_rocm_probe_env
|
||||
if command -v rocminfo >/dev/null 2>&1 && \
|
||||
rocminfo 2>/dev/null | awk '/Name:[[:space:]]*gfx1151/{found=1} END{exit !found}'; then
|
||||
rocminfo 2>/dev/null | awk '/Name:[[:space:]]*gfx[1-9]/ && !/generic/{found=1} END{exit !found}'; then
|
||||
# rocminfo may work only via the transient env _ensure_rocm_probe_env
|
||||
# just set, which dies with the installer. Persist the drop-in so login
|
||||
# shells (Studio, llama.cpp) inherit it -- else a reinstall over an
|
||||
|
|
@ -2349,9 +2377,12 @@ _maybe_bootstrap_rocm_wsl() {
|
|||
fi
|
||||
# WSL GPU passthrough device must exist (present on any WSL2 GPU host).
|
||||
[ -e /dev/dxg ] || return 0
|
||||
# Only Strix Halo (gfx1151): rocminfo can't tell us the arch yet, so match
|
||||
# the CPU model string WSL exposes (e.g. "AMD Ryzen AI Max+ ... Radeon 8060S").
|
||||
grep -qiE 'Ryzen AI Max|Radeon 80[0-9]0S|Strix Halo' /proc/cpuinfo 2>/dev/null || return 0
|
||||
# Strix APUs show in /proc/cpuinfo (the CPU model); discrete cards don't, so also
|
||||
# ask the Windows host. Either signal suffices; the bootstrap detects arch from rocminfo.
|
||||
if ! grep -qiE 'Ryzen AI Max|Radeon 80[0-9]0S|Strix Halo' /proc/cpuinfo 2>/dev/null \
|
||||
&& ! _wsl_amd_gpu_name >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
command -v bash >/dev/null 2>&1 || return 0
|
||||
|
||||
# Fast path: already configured (librocdxg present) but launched from a
|
||||
|
|
@ -2369,7 +2400,8 @@ _maybe_bootstrap_rocm_wsl() {
|
|||
fi
|
||||
|
||||
echo ""
|
||||
substep "Detected AMD Strix Halo (Radeon 8000S) in WSL with no ROCm runtime yet." "$C_WARN"
|
||||
_rw_gpu="$(_wsl_amd_gpu_name 2>/dev/null || true)"; [ -n "$_rw_gpu" ] || _rw_gpu="an AMD GPU"
|
||||
substep "Detected ${_rw_gpu} in WSL with no ROCm runtime yet." "$C_WARN"
|
||||
substep "Setting up ROCm-on-WSL (ROCm 7.2 + librocdxg) automatically to enable this GPU."
|
||||
substep "One-time, uses sudo and a large download. (skip: re-run with UNSLOTH_SKIP_ROCM_WSL_SETUP=1)"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@
|
|||
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved.
|
||||
#
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# Enable ROCm-on-WSL for AMD Strix Halo (Radeon 8060S / gfx1151)
|
||||
# Enable ROCm-on-WSL for AMD GPUs (Strix Halo/Point APUs AND discrete Radeon RX
|
||||
# 7000/9000). Verified on gfx1151 (Radeon 8060S) and gfx1200 (Radeon RX 9060 XT).
|
||||
# ──────────────────────────────────────────────────────────────────────────────
|
||||
# install.sh already routes gfx1151 to the right ROCm wheels once a ROCm runtime
|
||||
# is present; what it does NOT do is install AMD's ROCm userspace + the WSL DXG
|
||||
# bridge. This helper automates that Linux-side prerequisite on Ubuntu 24.04
|
||||
# WSL2 and is invoked by install.sh when it sees a Strix Halo APU in WSL (via
|
||||
# /dev/dxg) but no ROCm runtime yet. Fully idempotent (re-run just re-verifies).
|
||||
# install.sh routes the detected arch to the right ROCm wheels once a runtime exists;
|
||||
# what it does NOT do is install AMD's ROCm userspace + the WSL DXG bridge (librocdxg).
|
||||
# This helper does that Linux-side prerequisite on Ubuntu 24.04 WSL2, invoked by
|
||||
# install.sh when it sees an AMD GPU via /dev/dxg but no ROCm yet. Arch-agnostic: the
|
||||
# arch is auto-detected from rocminfo (override UNSLOTH_WSL_GFX=gfx1200). Idempotent.
|
||||
#
|
||||
# Manual, admin-gated Windows prerequisite: an AMD Adrenalin driver with
|
||||
# production ROCDXG/WSL support (26.2.2+). install.ps1 offers to update it. Once
|
||||
|
|
@ -34,10 +35,12 @@ set -euo pipefail
|
|||
|
||||
# ── Tunables (override via env) ──────────────────────────────────────────────
|
||||
ROCM_VER="${UNSLOTH_WSL_ROCM_VER:-7.2.1}" # ROCm release to install
|
||||
GFX="gfx1151"
|
||||
# GPU arch: empty = auto-detect from rocminfo after install (override UNSLOTH_WSL_GFX=gfx1200).
|
||||
# The ROCm + librocdxg setup is arch-agnostic; only verify + the smoke test need the arch.
|
||||
GFX="${UNSLOTH_WSL_GFX:-}"
|
||||
LIBROCDXG_REF="${UNSLOTH_LIBROCDXG_REF:-develop}" # ROCm/librocdxg git ref to build
|
||||
# AMD's gfx1151 wheel index (same one install.sh uses); only for the smoke test.
|
||||
TORCH_INDEX="${UNSLOTH_AMD_ROCM_MIRROR:-https://repo.amd.com/rocm/whl}/${GFX}/"
|
||||
# AMD's wheel index for the (optional) smoke test; resolved after arch detection.
|
||||
TORCH_INDEX=""
|
||||
# Optional torch smoke test (throwaway venv). OFF by default: install.sh installs
|
||||
# torch itself into the real venv right after, so a duplicate download is wasteful.
|
||||
SMOKE_TEST="${UNSLOTH_WSL_SMOKE_TEST:-0}"
|
||||
|
|
@ -220,12 +223,12 @@ $SUDO ldconfig
|
|||
say "Persisting ROCm-on-WSL environment"
|
||||
_envfile="/etc/profile.d/unsloth-rocm-wsl.sh"
|
||||
$SUDO tee "$_envfile" >/dev/null <<EOF
|
||||
# >>> Unsloth ROCm-on-WSL (gfx1151) >>>
|
||||
# >>> Unsloth ROCm-on-WSL >>>
|
||||
export HSA_ENABLE_DXG_DETECTION=1
|
||||
export TORCH_ROCM_AOTRITON_ENABLE_EXPERIMENTAL=1
|
||||
export PATH="${ROCM_DIR}/bin:\${PATH}"
|
||||
export LD_LIBRARY_PATH="${ROCM_DIR}/lib:\${LD_LIBRARY_PATH:-}"
|
||||
# <<< Unsloth ROCm-on-WSL (gfx1151) <<<
|
||||
# <<< Unsloth ROCm-on-WSL <<<
|
||||
EOF
|
||||
# also drop into ~/.bashrc for interactive shells
|
||||
if [ -n "${HOME:-}" ] && ! grep -q "Unsloth ROCm-on-WSL" "${HOME}/.bashrc" 2>/dev/null; then
|
||||
|
|
@ -237,32 +240,50 @@ export PATH="${ROCM_DIR}/bin:${PATH}"
|
|||
export LD_LIBRARY_PATH="${ROCM_DIR}/lib:${LD_LIBRARY_PATH:-}"
|
||||
|
||||
# ── Step 5: verify the runtime enumerates the GPU ────────────────────────────
|
||||
say "Verifying rocminfo sees ${GFX}"
|
||||
say "Verifying rocminfo enumerates the GPU over DXG"
|
||||
# Capture rocminfo into a var BEFORE grepping: piping into `grep -q` SIGPIPEs
|
||||
# rocminfo on first match, which under `set -o pipefail` turns a successful match
|
||||
# into a pipeline failure. Match the gfx1151 ISA "Name:" agent exactly (not a
|
||||
# broad gfx1[0-9]) so a generic fallback ISA or unrelated RDNA GPU can't pass.
|
||||
# into a pipeline failure.
|
||||
_rocminfo_out="$(rocminfo 2>/dev/null || true)"
|
||||
if ! printf '%s\n' "$_rocminfo_out" | grep -qE "Name:[[:space:]]*${GFX}([^0-9]|$)"; then
|
||||
# GPU agents advertise an ISA "Name: gfxNNNN". Match gfx[1-9] (excludes gfx000, the CPU
|
||||
# agent), drop the "gfx*-generic" fallback ISA, and take the first real GPU arch.
|
||||
_detected_gfx="$(printf '%s\n' "$_rocminfo_out" | grep -E 'Name:[[:space:]]*gfx[1-9]' | grep -v 'generic' | grep -oE 'gfx[1-9][0-9a-z]*' | head -1 || true)"
|
||||
if [ -z "$_detected_gfx" ]; then
|
||||
printf '%s\n' "$_rocminfo_out" | head -25 >&2 || true
|
||||
die "rocminfo did not enumerate a ${GFX} GPU agent. Most common cause: the Windows AMD driver predates production ROCDXG -- update Adrenalin (install.ps1 offers this), reboot, and re-run."
|
||||
die "rocminfo did not enumerate any GPU agent. Most common cause: the Windows AMD driver predates production ROCDXG -- update Adrenalin (install.ps1 offers this), reboot, and re-run."
|
||||
fi
|
||||
# Honour a caller-pinned arch (sanity-check via a consuming grep, not grep -q: under
|
||||
# pipefail -q would SIGPIPE printf on large output and misreport the arch); else adopt.
|
||||
if [ -n "$GFX" ] && ! printf '%s\n' "$_rocminfo_out" | grep -E "Name:[[:space:]]*${GFX}([^0-9]|$)" >/dev/null; then
|
||||
die "rocminfo enumerated '${_detected_gfx}' but not the requested UNSLOTH_WSL_GFX='${GFX}'."
|
||||
fi
|
||||
GFX="${GFX:-$_detected_gfx}"
|
||||
# Display-only summary: best-effort (|| true) so head's early pipe-close under
|
||||
# `set -o pipefail` can't fail the bootstrap after verification already passed.
|
||||
printf '%s\n' "$_rocminfo_out" | grep -E 'Marketing Name|Device Type|Compute Unit' | grep -iE "Radeon|GPU|Compute" | head -3 || true
|
||||
note "ROCm-on-WSL runtime is live for ${GFX}."
|
||||
|
||||
# ── Step 6 (optional): torch smoke test from the gfx1151 index ───────────────
|
||||
# ── Step 6 (optional): torch smoke test from AMD's per-arch wheel index ───────
|
||||
if [ "$SMOKE_TEST" = "1" ]; then
|
||||
say "Smoke-testing PyTorch on ${GFX} (throwaway venv)"
|
||||
# Map the detected arch to AMD's repo.amd.com wheel family index.
|
||||
case "$GFX" in
|
||||
gfx1200|gfx1201) _fam="gfx120X-all" ;;
|
||||
gfx1100|gfx1101|gfx1102|gfx1103) _fam="gfx110X-all" ;;
|
||||
*) _fam="$GFX" ;; # gfx1150/gfx1151/gfx90a: own index
|
||||
esac
|
||||
TORCH_INDEX="${UNSLOTH_AMD_ROCM_MIRROR:-https://repo.amd.com/rocm/whl}/${_fam}/"
|
||||
_venv="${HOME}/.unsloth/rocm-smoketest"
|
||||
rm -rf "$_venv"; python3 -m venv "$_venv"
|
||||
"$_venv/bin/pip" install --quiet --upgrade pip
|
||||
# gfx1151 index is primary (torch + triton); PyPI only an extra for pure-py
|
||||
# AMD arch index is primary (torch + triton); PyPI only an extra for pure-py
|
||||
# deps. The constraint keeps pip on the ROCm wheel, not a newer PyPI CUDA torch.
|
||||
"$_venv/bin/pip" install --index-url "$TORCH_INDEX" \
|
||||
--extra-index-url https://pypi.org/simple "$TORCH_CONSTRAINT" || \
|
||||
die "torch install from ${TORCH_INDEX} failed."
|
||||
# WSL: torch's bundled ROCr must load the DXG bridge -- drop librocdxg into torch/lib.
|
||||
_tlib="$("$_venv/bin/python" -c 'import torch,os;print(os.path.join(os.path.dirname(torch.__file__),"lib"))' 2>/dev/null || true)"
|
||||
[ -d "$_tlib" ] && cp -f "${ROCM_DIR}"/lib/librocdxg.so* "$_tlib"/ 2>/dev/null || true
|
||||
"$_venv/bin/python" - <<'PY'
|
||||
import torch
|
||||
ok = torch.cuda.is_available()
|
||||
|
|
|
|||
|
|
@ -3426,8 +3426,9 @@ class TestInstallShDropinPersistence:
|
|||
def test_gate5_early_return_persists_dropin(self):
|
||||
"""The rocminfo-already-works early return must call the persist helper before returning."""
|
||||
source = _INSTALL_SH_PATH.read_text(encoding = "utf-8")
|
||||
# The persist call must precede `return 0` at the rocminfo gfx1151 gate.
|
||||
gate = source.find("Name:[[:space:]]*gfx1151")
|
||||
# The persist call must precede `return 0` at the rocminfo GPU-agent gate
|
||||
# (uniquely identified by the `!/generic/` clause the other probes lack).
|
||||
gate = source.find("Name:[[:space:]]*gfx[1-9]/ && !/generic/")
|
||||
assert gate != -1
|
||||
window = source[gate : gate + 900]
|
||||
assert "_persist_rocm_wsl_dropin" in window
|
||||
|
|
@ -3441,6 +3442,49 @@ class TestInstallShDropinPersistence:
|
|||
assert "profile.d/unsloth-rocm-wsl.sh" in body
|
||||
|
||||
|
||||
_STRIXHALO_WSL_PATH = PACKAGE_ROOT / "scripts" / "install_rocm_wsl_strixhalo.sh"
|
||||
|
||||
|
||||
class TestWslRerouteNvidiaGuard:
|
||||
"""_maybe_reroute_strixhalo_to_2404 must skip the AMD reroute on hybrid AMD+NVIDIA hosts by
|
||||
reusing _has_usable_nvidia_gpu (CUDA_VISIBLE_DEVICES-aware + /proc/driver/nvidia fallback),
|
||||
which must be defined before the reroute's call site so it is actually available."""
|
||||
|
||||
def test_reroute_calls_nvidia_helper_before_amd_signal(self):
|
||||
source = _INSTALL_SH_PATH.read_text(encoding = "utf-8")
|
||||
start = source.find("_maybe_reroute_strixhalo_to_2404()")
|
||||
assert start != -1
|
||||
body = source[start : start + 1200]
|
||||
nv = body.find("_has_usable_nvidia_gpu")
|
||||
wmi = body.find("_wsl_amd_gpu_name")
|
||||
assert nv != -1, "reroute must consult _has_usable_nvidia_gpu before deciding to reroute"
|
||||
assert wmi != -1
|
||||
# The NVIDIA guard must precede the AMD/WMI signal and return early.
|
||||
assert nv < wmi
|
||||
assert body.find("return 0", nv) < wmi
|
||||
|
||||
def test_nvidia_helper_and_deps_defined_before_reroute_callsite(self):
|
||||
source = _INSTALL_SH_PATH.read_text(encoding = "utf-8")
|
||||
call = source.find("\n_maybe_reroute_strixhalo_to_2404 || true")
|
||||
assert call != -1
|
||||
for fn in ("_run_bounded() {", "_cvd_hides_nvidia() {", "_has_usable_nvidia_gpu() {"):
|
||||
idx = source.find(fn)
|
||||
assert idx != -1 and idx < call, f"{fn} must be defined before the reroute call"
|
||||
|
||||
|
||||
class TestStrixhaloGfxOverridePipefail:
|
||||
"""The UNSLOTH_WSL_GFX override check must use a consuming grep, not grep -q: under
|
||||
`set -o pipefail` an early -q exit SIGPIPEs printf and misreports the arch on large output."""
|
||||
|
||||
def test_gfx_override_uses_consuming_grep(self):
|
||||
source = _STRIXHALO_WSL_PATH.read_text(encoding = "utf-8")
|
||||
idx = source.find('grep -E "Name:[[:space:]]*${GFX}')
|
||||
assert idx != -1, "GFX override must use a consuming grep -E (not grep -q)"
|
||||
line = source[idx : source.find("\n", idx)]
|
||||
assert ">/dev/null" in line
|
||||
assert 'grep -qE "Name:[[:space:]]*${GFX}' not in source
|
||||
|
||||
|
||||
class TestLlamaCppRuntimeWslOrdering:
|
||||
"""The serve-time launcher mirrors binary_env: system HIP before the bundle dir on WSL."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue