Skip to content
Open
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
80 changes: 80 additions & 0 deletions .github/workflows/tests_archdetect_amd_gpu.yml
Original file line number Diff line number Diff line change
@@ -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/<name>/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
137 changes: 116 additions & 21 deletions init/eessi_archdetect.sh
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,107 @@ 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)
# $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}"
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
Expand All @@ -189,28 +285,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
Expand Down
1 change: 1 addition & 0 deletions tests/archdetect/amd-smi/none.output
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
non-zero exit code: 2
1 change: 1 addition & 0 deletions tests/archdetect/amd-smi/v710.output
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
accel/amd/gfx1101
19 changes: 19 additions & 0 deletions tests/archdetect/amd-smi/v710.sh
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tests/archdetect/kfd/cpu_only/expected.output
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
non-zero exit code: 2
30 changes: 30 additions & 0 deletions tests/archdetect/kfd/cpu_only/nodes/0/properties
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions tests/archdetect/kfd/v710/expected.output
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
accel/amd/gfx1101
30 changes: 30 additions & 0 deletions tests/archdetect/kfd/v710/nodes/0/properties
Original file line number Diff line number Diff line change
@@ -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
38 changes: 38 additions & 0 deletions tests/archdetect/kfd/v710/nodes/1/properties
Original file line number Diff line number Diff line change
@@ -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