From df5b2715ae3603efc73687aa30350dfabdc100c8 Mon Sep 17 00:00:00 2001 From: Aayush Joglekar Date: Thu, 23 Apr 2026 12:12:45 +0200 Subject: [PATCH 1/2] Add AMD accelerator support and refactor accelpath method --- init/eessi_archdetect.sh | 136 +++++++++++++++++++++++++++++++++------ 1 file changed, 115 insertions(+), 21 deletions(-) diff --git a/init/eessi_archdetect.sh b/init/eessi_archdetect.sh index f8534b09..0e7551ce 100755 --- a/init/eessi_archdetect.sh +++ b/init/eessi_archdetect.sh @@ -175,11 +175,106 @@ cpupath(){ fi } +nvidia_accelpath() { + # Check for NVIDIA GPUs via nvidia-smi command + local nvidia_smi + nvidia_smi=$(command -v nvidia-smi) + + if [[ $? -eq 0 ]]; then + log "DEBUG" "nvidia_accelpath: nvidia-smi command found @ ${nvidia_smi}" + local nvidia_smi_out + nvidia_smi_out=$(mktemp -p /tmp nvidia_smi_out.XXXXX) + + nvidia-smi --query-gpu=gpu_name,count,driver_version,compute_cap --format=csv,noheader 2>&1 > $nvidia_smi_out + if [[ $? -eq 0 ]]; then + local nvidia_smi_info=$(head -n 1 $nvidia_smi_out) + local cuda_cc=$(echo $nvidia_smi_info | sed 's/, /,/g' | cut -f4 -d, | sed 's/\.//g') + log "DEBUG" "nvidia_accelpath: CUDA compute capability '${cuda_cc}' derived from nvidia-smi output '${nvidia_smi_info}'" + + echo "accel/nvidia/cc${cuda_cc}" + rm -f $nvidia_smi_out + return 0 + else + log "DEBUG" "nvidia_accelpath: nvidia-smi command failed, see output in $nvidia_smi_out" + return 3 + fi + else + log "DEBUG" "nvidia_accelpath: nvidia-smi command not found" + return 2 + fi +} + +amd_accelpath() { + # Method 1: Check for AMD GPUs via KFD sysfs interface (No amd-smi or Python required) + local kfd_nodes="/sys/devices/virtual/kfd/kfd/topology/nodes" + + if [[ -d "$kfd_nodes" ]]; then + log "DEBUG" "amd_accelpath: KFD sysfs path found @ ${kfd_nodes}" + local amdgcn_cc="" + + # ls -1v ensures numeric/version sorting (nodes/0, nodes/1, ..., nodes/10) + for node in $(ls -1v "$kfd_nodes" 2>/dev/null); do + local prop_file="$kfd_nodes/$node/properties" + + if [[ -f "$prop_file" ]]; then + # Extract the integer value. 2>/dev/null suppresses read errors. + local gfx_ver=$(grep "^gfx_target_version" "$prop_file" 2>/dev/null | awk '{print $2}') + + # If gfx_ver is non-empty and greater than 0 (0 means it's a CPU node) + if [[ -n "$gfx_ver" && "$gfx_ver" -gt 0 ]]; then + local major=$(( (gfx_ver / 10000) % 100 )) + local minor=$(( (gfx_ver / 100) % 100 )) + local step=$(( gfx_ver % 100 )) + + amdgcn_cc=$(printf "gfx%d%d%x" $major $minor $step) + log "DEBUG" "amd_accelpath: AMDGCN compute capability '${amdgcn_cc}' derived from KFD node ${node}" + break + fi + fi + done + + if [[ -n "$amdgcn_cc" ]]; then + echo "accel/amd/${amdgcn_cc}" + return 0 + fi + log "DEBUG" "amd_accelpath: KFD topology found, but no AMD GPUs detected. Falling back to amd-smi." + else + log "DEBUG" "amd_accelpath: KFD sysfs path not found. Falling back to amd-smi." + fi + + # Method 2: Fallback to AMD GPUs via amd-smi command using /tmp files + local amd_smi + amd_smi=$(command -v amd-smi) + + if [[ $? -eq 0 ]]; then + log "DEBUG" "amd_accelpath: amd-smi command found @ ${amd_smi}" + local amd_smi_out + amd_smi_out=$(mktemp -p /tmp amd_smi_out.XXXXX) + + amd-smi static --asic | grep TARGET_GRAPHICS_VERSION 2>&1 > $amd_smi_out + if [[ $? -eq 0 ]]; then + local amd_smi_info=$(head -n 1 $amd_smi_out) + local amdgcn_cc=$(echo $amd_smi_info | sed 's/.*: //') + log "DEBUG" "amd_accelpath: AMDGCN compute capability '${amdgcn_cc}' derived from amd-smi output '${amd_smi_info}'" + + echo "accel/amd/${amdgcn_cc}" + rm -f $amd_smi_out + return 0 + else + log "DEBUG" "amd_accelpath: amd-smi command failed, see output in $amd_smi_out" + return 3 + fi + else + log "DEBUG" "amd_accelpath: amd-smi command not found" + return 2 + fi +} + accelpath() { # If EESSI_ACCELERATOR_TARGET_OVERRIDE is set, use it log "DEBUG" "accelpath: Override variable set as '$EESSI_ACCELERATOR_TARGET_OVERRIDE' " if [ ! -z $EESSI_ACCELERATOR_TARGET_OVERRIDE ]; then - # Regex that allows both NVIDIA and AMD overrides + # Updated regex to allow both NVIDIA and AMD overrides if [[ "$EESSI_ACCELERATOR_TARGET_OVERRIDE" =~ ^accel/(nvidia/cc[0-9]+|amd/gfx[0-9a-f]+)$ ]]; then echo "$EESSI_ACCELERATOR_TARGET_OVERRIDE" return 0 @@ -189,28 +284,27 @@ accelpath() { fi fi - # check for NVIDIA GPUs via nvidia-smi command - nvidia_smi=$(command -v nvidia-smi) + # 1. Check for NVIDIA GPUs + local nv_res + nv_res=$(nvidia_accelpath) if [[ $? -eq 0 ]]; then - log "DEBUG" "accelpath: nvidia-smi command found @ ${nvidia_smi}" - nvidia_smi_out=$(mktemp -p /tmp nvidia_smi_out.XXXXX) - nvidia-smi --query-gpu=gpu_name,count,driver_version,compute_cap --format=csv,noheader 2>&1 > $nvidia_smi_out - if [[ $? -eq 0 ]]; then - nvidia_smi_info=$(head -n 1 $nvidia_smi_out) - cuda_cc=$(echo $nvidia_smi_info | sed 's/, /,/g' | cut -f4 -d, | sed 's/\.//g') - log "DEBUG" "accelpath: CUDA compute capability '${cuda_cc}' derived from nvidia-smi output '${nvidia_smi_info}'" - res="accel/nvidia/cc${cuda_cc}" - log "DEBUG" "accelpath: result: ${res}" - echo $res - rm -f $nvidia_smi_out - else - log "DEBUG" "accelpath: nvidia-smi command failed, see output in $nvidia_smi_out" - exit 3 - fi - else - log "DEBUG" "accelpath: nvidia-smi command not found" - exit 2 + log "DEBUG" "accelpath: result: ${nv_res}" + echo "$nv_res" + return 0 fi + + # 2. Check for AMD GPUs + local amd_res + amd_res=$(amd_accelpath) + if [[ $? -eq 0 ]]; then + log "DEBUG" "accelpath: result: ${amd_res}" + echo "$amd_res" + return 0 + fi + + # 3. Fail gracefully if neither is found + log "DEBUG" "accelpath: No supported accelerators found on this system." + exit 2 } # Parse command line arguments From cf6d607b94b7a43b9068171a39ea7a1947d69c28 Mon Sep 17 00:00:00 2001 From: humeilan_microsoft Date: Sat, 25 Jul 2026 09:49:47 +0200 Subject: [PATCH 2/2] Add CI tests for AMD GPU accelerator detection Adds workflow-based tests for both AMD detection methods in accelpath, using output captured from real hardware (AMD Radeon Pro V710, gfx1101) so no AMD GPU is required in CI: - Method 1 (KFD sysfs): make the topology root overridable via $EESSI_KFD_TOPOLOGY_ROOT so captured nodes/*/properties fixtures can be injected. Covers the gfx_target_version -> gfxMAJMINSTEP decode and the CPU-node (gfx_target_version 0) skip. - Method 2 (amd-smi): fake amd-smi stub on $PATH replaying real 'amd-smi static --asic' output, mirroring the existing nvidia-smi tests. New workflow tests_archdetect_amd_gpu.yml runs kfd_sysfs (v710, cpu_only) and amd_smi (v710) matrices. NVIDIA detection and the no-accelerator exit-2 path remain unchanged. This work was created with the help of AI (GitHub Copilot / an AI coding assistant). --- .../workflows/tests_archdetect_amd_gpu.yml | 80 +++++++++++++++++++ init/eessi_archdetect.sh | 3 +- tests/archdetect/amd-smi/none.output | 1 + tests/archdetect/amd-smi/v710.output | 1 + tests/archdetect/amd-smi/v710.sh | 19 +++++ tests/archdetect/kfd/cpu_only/expected.output | 1 + .../kfd/cpu_only/nodes/0/properties | 30 +++++++ tests/archdetect/kfd/v710/expected.output | 1 + tests/archdetect/kfd/v710/nodes/0/properties | 30 +++++++ tests/archdetect/kfd/v710/nodes/1/properties | 38 +++++++++ 10 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/tests_archdetect_amd_gpu.yml create mode 100644 tests/archdetect/amd-smi/none.output create mode 100644 tests/archdetect/amd-smi/v710.output create mode 100755 tests/archdetect/amd-smi/v710.sh create mode 100644 tests/archdetect/kfd/cpu_only/expected.output create mode 100644 tests/archdetect/kfd/cpu_only/nodes/0/properties create mode 100644 tests/archdetect/kfd/v710/expected.output create mode 100644 tests/archdetect/kfd/v710/nodes/0/properties create mode 100644 tests/archdetect/kfd/v710/nodes/1/properties diff --git a/.github/workflows/tests_archdetect_amd_gpu.yml b/.github/workflows/tests_archdetect_amd_gpu.yml new file mode 100644 index 00000000..932bed22 --- /dev/null +++ b/.github/workflows/tests_archdetect_amd_gpu.yml @@ -0,0 +1,80 @@ +# documentation: https://help.github.com/en/articles/workflow-syntax-for-github-actions +name: Tests for accelerator detection (AMD GPU) +on: + push: + pull_request: +permissions: + contents: read # to fetch code (actions/checkout) +jobs: + # Method 1: KFD sysfs topology parsing (no amd-smi / Python required). + # Fixtures under tests/archdetect/kfd//nodes/ are captured from real hardware + # and injected via $EESSI_KFD_TOPOLOGY_ROOT, so no AMD GPU is needed in CI. + kfd_sysfs: + runs-on: ubuntu-24.04 + strategy: + matrix: + fixture: + - v710 # AMD Radeon Pro V710 (NAVI32), gfx_target_version 110001 -> gfx1101 + - cpu_only # only a CPU node (gfx_target_version 0) -> no accelerator, exit 2 + fail-fast: false + steps: + - name: checkout + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + + - name: test AMD KFD sysfs accelerator detection + run: | + fixture="${{matrix.fixture}}" + root="$PWD/tests/archdetect/kfd/${fixture}/nodes" + expected=$(cat "$PWD/tests/archdetect/kfd/${fixture}/expected.output") + + # ensure no real amd-smi leaks in and lets the cpu_only case pass via Method 2 + export PATH="$(mktemp -d):/usr/bin:/bin" + + export EESSI_KFD_TOPOLOGY_ROOT="$root" + + # first run with debugging enabled, just to show the output + ./init/eessi_archdetect.sh -d accelpath || echo "non-zero exit code: $?" + + out=$(./init/eessi_archdetect.sh accelpath || echo "non-zero exit code: $?") + if [[ "$out" == "$expected" ]]; then + echo "KFD test for '${fixture}' PASSED: '$out'" + else + echo "KFD test for '${fixture}' FAILED: got '$out', expected '$expected'" >&2 + exit 1 + fi + + # Method 2: amd-smi fallback. A fake amd-smi (captured real-hardware output) is placed + # on $PATH, and the KFD root is pointed at an empty dir to force the fallback path. + amd_smi: + runs-on: ubuntu-24.04 + strategy: + matrix: + fixture: + - v710 # amd-smi reports TARGET_GRAPHICS_VERSION: gfx1101 + fail-fast: false + steps: + - name: checkout + uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + + - name: test AMD amd-smi accelerator detection + run: | + fixture="${{matrix.fixture}}" + expected=$(cat "$PWD/tests/archdetect/amd-smi/${fixture}.output") + + # force Method 1 (KFD) to find nothing so Method 2 (amd-smi) is exercised + export EESSI_KFD_TOPOLOGY_ROOT="$(mktemp -d)" + + # put fake amd-smi command in place + tmpdir=$(mktemp -d) + ln -s "$PWD/tests/archdetect/amd-smi/${fixture}.sh" "$tmpdir/amd-smi" + export PATH="$tmpdir:$PATH" + + ./init/eessi_archdetect.sh -d accelpath || echo "non-zero exit code: $?" + + out=$(./init/eessi_archdetect.sh accelpath || echo "non-zero exit code: $?") + if [[ "$out" == "$expected" ]]; then + echo "amd-smi test for '${fixture}' PASSED: '$out'" + else + echo "amd-smi test for '${fixture}' FAILED: got '$out', expected '$expected'" >&2 + exit 1 + fi diff --git a/init/eessi_archdetect.sh b/init/eessi_archdetect.sh index 0e7551ce..e2f9650f 100755 --- a/init/eessi_archdetect.sh +++ b/init/eessi_archdetect.sh @@ -206,7 +206,8 @@ nvidia_accelpath() { amd_accelpath() { # Method 1: Check for AMD GPUs via KFD sysfs interface (No amd-smi or Python required) - local kfd_nodes="/sys/devices/virtual/kfd/kfd/topology/nodes" + # $EESSI_KFD_TOPOLOGY_ROOT overrides the sysfs root so CI can test with captured fixtures. + local kfd_nodes="${EESSI_KFD_TOPOLOGY_ROOT:-/sys/devices/virtual/kfd/kfd/topology/nodes}" if [[ -d "$kfd_nodes" ]]; then log "DEBUG" "amd_accelpath: KFD sysfs path found @ ${kfd_nodes}" diff --git a/tests/archdetect/amd-smi/none.output b/tests/archdetect/amd-smi/none.output new file mode 100644 index 00000000..e287574c --- /dev/null +++ b/tests/archdetect/amd-smi/none.output @@ -0,0 +1 @@ +non-zero exit code: 2 diff --git a/tests/archdetect/amd-smi/v710.output b/tests/archdetect/amd-smi/v710.output new file mode 100644 index 00000000..41273630 --- /dev/null +++ b/tests/archdetect/amd-smi/v710.output @@ -0,0 +1 @@ +accel/amd/gfx1101 diff --git a/tests/archdetect/amd-smi/v710.sh b/tests/archdetect/amd-smi/v710.sh new file mode 100755 index 00000000..bc3d8b26 --- /dev/null +++ b/tests/archdetect/amd-smi/v710.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# output from AMD Radeon Pro V710 (NAVI32, gfx1101) system, +# produced by: amd-smi static --asic +cat <<'ASIC' +GPU: 0 + ASIC: + MARKET_NAME: NAVI32 + VENDOR_ID: 0x1002 + VENDOR_NAME: Advanced Micro Devices Inc. [AMD/ATI] + SUBVENDOR_ID: 0x1002 + DEVICE_ID: 0x7461 + SUBSYSTEM_ID: 0x0e34 + REV_ID: 0x00 + ASIC_SERIAL: 0xF7BD1622840AAC82 + OAM_ID: N/A + NUM_COMPUTE_UNITS: 54 + TARGET_GRAPHICS_VERSION: gfx1101 +ASIC +exit 0 diff --git a/tests/archdetect/kfd/cpu_only/expected.output b/tests/archdetect/kfd/cpu_only/expected.output new file mode 100644 index 00000000..e287574c --- /dev/null +++ b/tests/archdetect/kfd/cpu_only/expected.output @@ -0,0 +1 @@ +non-zero exit code: 2 diff --git a/tests/archdetect/kfd/cpu_only/nodes/0/properties b/tests/archdetect/kfd/cpu_only/nodes/0/properties new file mode 100644 index 00000000..92a4d8ed --- /dev/null +++ b/tests/archdetect/kfd/cpu_only/nodes/0/properties @@ -0,0 +1,30 @@ +cpu_cores_count 24 +simd_count 0 +mem_banks_count 1 +caches_count 0 +io_links_count 1 +p2p_links_count 0 +cpu_core_id_base 0 +simd_id_base 0 +max_waves_per_simd 0 +lds_size_in_kb 0 +gds_size_in_kb 0 +num_gws 0 +wave_front_size 0 +array_count 0 +simd_arrays_per_engine 0 +cu_per_simd_array 0 +simd_per_cu 0 +max_slots_scratch_cu 0 +gfx_target_version 0 +vendor_id 0 +device_id 0 +location_id 0 +domain 0 +drm_render_minor 0 +hive_id 0 +num_sdma_engines 0 +num_sdma_xgmi_engines 0 +num_sdma_queues_per_engine 0 +num_cp_queues 0 +max_engine_clk_ccompute 0 diff --git a/tests/archdetect/kfd/v710/expected.output b/tests/archdetect/kfd/v710/expected.output new file mode 100644 index 00000000..41273630 --- /dev/null +++ b/tests/archdetect/kfd/v710/expected.output @@ -0,0 +1 @@ +accel/amd/gfx1101 diff --git a/tests/archdetect/kfd/v710/nodes/0/properties b/tests/archdetect/kfd/v710/nodes/0/properties new file mode 100644 index 00000000..92a4d8ed --- /dev/null +++ b/tests/archdetect/kfd/v710/nodes/0/properties @@ -0,0 +1,30 @@ +cpu_cores_count 24 +simd_count 0 +mem_banks_count 1 +caches_count 0 +io_links_count 1 +p2p_links_count 0 +cpu_core_id_base 0 +simd_id_base 0 +max_waves_per_simd 0 +lds_size_in_kb 0 +gds_size_in_kb 0 +num_gws 0 +wave_front_size 0 +array_count 0 +simd_arrays_per_engine 0 +cu_per_simd_array 0 +simd_per_cu 0 +max_slots_scratch_cu 0 +gfx_target_version 0 +vendor_id 0 +device_id 0 +location_id 0 +domain 0 +drm_render_minor 0 +hive_id 0 +num_sdma_engines 0 +num_sdma_xgmi_engines 0 +num_sdma_queues_per_engine 0 +num_cp_queues 0 +max_engine_clk_ccompute 0 diff --git a/tests/archdetect/kfd/v710/nodes/1/properties b/tests/archdetect/kfd/v710/nodes/1/properties new file mode 100644 index 00000000..d1c0c51e --- /dev/null +++ b/tests/archdetect/kfd/v710/nodes/1/properties @@ -0,0 +1,38 @@ +cpu_cores_count 0 +simd_count 108 +mem_banks_count 1 +caches_count 116 +io_links_count 1 +p2p_links_count 0 +cpu_core_id_base 0 +simd_id_base 2147487744 +max_waves_per_simd 16 +lds_size_in_kb 64 +gds_size_in_kb 0 +num_gws 64 +wave_front_size 32 +array_count 6 +simd_arrays_per_engine 2 +cu_per_simd_array 10 +simd_per_cu 2 +max_slots_scratch_cu 32 +gfx_target_version 110001 +vendor_id 4098 +device_id 29793 +location_id 0 +domain 2 +drm_render_minor 128 +hive_id 0 +num_sdma_engines 2 +num_sdma_xgmi_engines 0 +num_sdma_queues_per_engine 6 +num_cp_queues 14 +max_engine_clk_fcompute 2585 +local_mem_size 0 +fw_version 2460 +capability 734503552 +debug_prop 1495 +sdma_fw_version 25 +unique_id 17851448835466505346 +num_xcc 1 +max_engine_clk_ccompute 0