From 77e958b4e0dc6c4c747afc94772328b40cbe532c Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Wed, 11 Nov 2026 20:18:31 +0100 Subject: [PATCH 1/2] Type GPUs by PCI ID and install the right NVIDIA packages on Ubuntu GPU vendors come from the PCI vendor ID, and Intel's discrete cards (DG1, Alchemist, Battlemage) from the device ID. Names depend on the pci.ids at hand: a Battlemage card was "Device e212" on Ubuntu 24.04 and took the Gen9+ path. On Ubuntu the NVIDIA branch asked for libcuda1, which does not exist there, and then for libnvidia-decode-, which NVIDIA stopped publishing with 590. It now picks libnvidia-compute with or without the series in its name, whichever has the host's release, pins that version and installs with the same flags as Debian. The version pin also covers nvidia-*, since libnvidia-compute needs nvidia-persistenced at exactly its own version. --- lib/API.txt | 1 + lib/hwaccel.func | 117 +++++++++++++++++++----------------- tests/gpu-pci-class.sh | 13 ++++ tests/nvidia-version-pin.sh | 16 +++-- 4 files changed, 87 insertions(+), 60 deletions(-) diff --git a/lib/API.txt b/lib/API.txt index 2d3fe273..ae518acd 100644 --- a/lib/API.txt +++ b/lib/API.txt @@ -36,6 +36,7 @@ _forge_workdir _gh_scan_older_releases _github_release_noapi _gl_scan_older_releases +_gpu_type _install_uvx_wrapper _mongodb_in_place_precheck _mongodb_in_place_simulate diff --git a/lib/hwaccel.func b/lib/hwaccel.func index 9f2cf1f9..311afbfa 100644 --- a/lib/hwaccel.func +++ b/lib/hwaccel.func @@ -19,6 +19,28 @@ _TOOLS_HWACCEL_LOADED=1 # single warning line. The batch is still tried first -- one apt run, and the # normal case -- and only a failure falls back to one at a time, which is also # the only way to name the package that was actually missing. +# Vendor and Intel's discrete cards by PCI ID ("8086:e20b"): the name depends +# on the pci.ids at hand, and a new card has none yet. +_gpu_type() { + local id="$1" name="$2" + case "${id%%:*}" in + 10de) echo "NVIDIA" ;; + 1002) echo "AMD" ;; + 8086) + # DG1 4905-4909, Alchemist 56xx, Battlemage e2xx + if [[ "${id#*:}" =~ ^(490[5-9]|56|e2) ]] || grep -qiE 'Arc|DG[12]' <<<"$name"; then + echo "INTEL_ARC" + elif grep -qiE 'UHD|Iris|HD Graphics [5-6][0-9]{2}([^0-9]|$)' <<<"$name"; then + echo "INTEL_GEN9+" + elif grep -qiE 'HD Graphics [2-5][0-9]{3}' <<<"$name"; then + echo "INTEL_LEGACY" + else + echo "INTEL" + fi + ;; + esac +} + _cs_apt_install_optional() { (($#)) || return 0 $STD apt -y install "$@" 2>/dev/null && return 0 @@ -99,35 +121,16 @@ setup_hwaccel() { [[ -z "$line" ]] && continue local pci_addr gpu_name gpu_type="" + local gpu_id pci_addr=$(echo "$line" | awk '{print $1}') - gpu_name=$(echo "$line" | sed 's/^[^ ]* [^:]*: //') - - # Determine GPU type - # Note: Use -w (word boundary) for ATI to avoid matching "CorporATIon" - if echo "$gpu_name" | grep -qi 'Intel'; then - gpu_type="INTEL" - # Subtype detection for Intel - # Order matters: Check Arc first, then Gen9+ (UHD/Iris/HD 5xx-6xx), then Legacy (HD 2xxx-5xxx) - # HD Graphics 530/630 = Gen 9 (Skylake/Kaby Lake) - 3 digits - # HD Graphics 4600/5500 = Gen 7-8 (Haswell/Broadwell) - 4 digits starting with 2-5 - if echo "$gpu_name" | grep -qiE 'Arc|DG[12]'; then - gpu_type="INTEL_ARC" - elif echo "$gpu_name" | grep -qiE 'UHD|Iris|HD Graphics [5-6][0-9]{2}[^0-9]|HD Graphics [5-6][0-9]{2}$'; then - # HD Graphics 5xx/6xx (3 digits) = Gen 9+ (Skylake onwards) - gpu_type="INTEL_GEN9+" - elif echo "$gpu_name" | grep -qiE 'HD Graphics [2-5][0-9]{3}'; then - # HD Graphics 2xxx-5xxx (4 digits) = Gen 6-8 Legacy - gpu_type="INTEL_LEGACY" - fi - elif echo "$gpu_name" | grep -qiwE 'AMD|ATI|Radeon|Advanced Micro Devices'; then - gpu_type="AMD" - elif echo "$gpu_name" | grep -qi 'NVIDIA'; then - # lspci shows the host's bus; the card is only ours with its device nodes. - if compgen -G '/dev/nvidia[0-9]*' >/dev/null; then - gpu_type="NVIDIA" - else - msg_custom "ℹ️" "${YW}" "NVIDIA GPU on the host, but no /dev/nvidia* device in this container - skipping it (add the devices to the container config to use it)" - fi + gpu_id=$(grep -oE '\[[0-9a-f]{4}:[0-9a-f]{4}\]' <<<"$line" | tail -1 | tr -d '[]' || true) + gpu_name=$(sed -E 's/^[^ ]* [^:]*: //; s/ \[[0-9a-f]{4}:[0-9a-f]{4}\]//' <<<"$line") + gpu_type="$(_gpu_type "$gpu_id" "$gpu_name")" + + # lspci shows the host's bus; the card is only ours with its device nodes. + if [[ "$gpu_type" == "NVIDIA" ]] && ! compgen -G '/dev/nvidia[0-9]*' >/dev/null; then + gpu_type="" + msg_custom "ℹ️" "${YW}" "NVIDIA GPU on the host, but no /dev/nvidia* device in this container - skipping it (add the devices to the container config to use it)" fi if [[ -n "$gpu_type" ]]; then @@ -137,7 +140,7 @@ setup_hwaccel() { ((gpu_count++)) || true fi # The class field only: the whole line matched "Device 43d0" and "SanDisk Ultra 3D". - done < <(lspci 2>/dev/null | grep -E '^[^ ]+ (VGA compatible|XGA compatible|3D|Display) controller[ :]') + done < <(lspci -nn 2>/dev/null | grep -E '^[^ ]+ (VGA compatible|XGA compatible|3D|Display) controller[ :]') # Check for AMD APU via CPU vendor if no discrete GPU found local cpu_vendor @@ -678,8 +681,8 @@ ENVEOF # The host's exact release first; the series fallback may end up newer than # the kernel module, which the caller warns about. _cuda_pick_version() { - local host="$1" madison exact - madison=$(apt-cache madison libcuda1 2>/dev/null | awk '{print $3}') || return 0 + local host="$1" pkg="${2:-libcuda1}" madison exact + madison=$(apt-cache madison "$pkg" 2>/dev/null | awk '{print $3}') || return 0 exact=$(printf '%s\n' "$madison" | grep -E "^${host//./\\.}(-|$)" | head -1 || true) if [[ -n "$exact" ]]; then printf '%s\n' "$exact" @@ -692,7 +695,7 @@ _cuda_pick_version() { # The priority has to stay above the origin pin's. _nvidia_version_pin() { cat </dev/null || msg_warn "apt update failed - continuing anyway" - # Extract major version for flexible matching local nvidia_major_version="${nvidia_host_version%%.*}" - # Check what versions are available - local available_version="" - available_version=$(apt-cache madison libcuda1 2>/dev/null | awk '{print $3}' | grep -E "^${nvidia_major_version}\." | head -1 || true) + # Ubuntu has no libcuda1: libnvidia-compute carries it. NVIDIA names it with + # the series up to 580 and without from 590 on, and Ubuntu's own archive + # keeps the series name. The one that has the host's release wins. + local nv_suffix="" available_version="" cand cand_version + for cand in "-${nvidia_major_version}" ""; do + cand_version="$(_cuda_pick_version "$nvidia_host_version" "libnvidia-compute${cand}")" + [[ -n "$cand_version" ]] || continue + if [[ -z "$available_version" || "$cand_version" == "${nvidia_host_version}-"* ]]; then + nv_suffix="$cand" + available_version="$cand_version" + fi + [[ "$available_version" == "${nvidia_host_version}-"* ]] && break + done if [[ -n "$available_version" ]]; then + _nvidia_version_pin "$available_version" >/etc/apt/preferences.d/nvidia-cuda-pin msg_info "Installing NVIDIA libraries (version ${available_version})" - local nvidia_pkgs="libcuda1=${available_version} libnvcuvid1=${available_version} libnvidia-encode1=${available_version} libnvidia-ml1=${available_version}" - if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends $nvidia_pkgs 2>/dev/null; then - # Same as above: only the major was compared. + local nvidia_pkgs="libnvidia-compute${nv_suffix}=${available_version} libnvidia-decode${nv_suffix}=${available_version} libnvidia-encode${nv_suffix}=${available_version}" + local -a apt_install=(apt-get -y --allow-downgrades -o Dpkg::Options::="--force-confold" install --no-install-recommends) + if $STD "${apt_install[@]}" $nvidia_pkgs 2>/dev/null; then if [[ "$available_version" == "${nvidia_host_version}"* ]]; then msg_ok "Installed NVIDIA libraries ${available_version} (host ${nvidia_host_version})" else msg_warn "NVIDIA libraries ${available_version} do not match the host driver ${nvidia_host_version}" msg_warn "CUDA workloads usually fail on a mismatch. Install the matching driver on the host, or pick a container OS whose repos carry it." fi + $STD "${apt_install[@]}" "nvidia-utils${nv_suffix}=${available_version}" 2>/dev/null || true else - # Fallback to Ubuntu repo packages with versioned nvidia-utils - msg_warn "CUDA repo install failed - trying Ubuntu native packages (nvidia-utils-${nvidia_major_version})" - if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends \ - libnvidia-decode-${nvidia_major_version} libnvidia-encode-${nvidia_major_version} nvidia-utils-${nvidia_major_version} 2>/dev/null; then - msg_ok "Installed Ubuntu NVIDIA packages (${nvidia_major_version})" - else - msg_warn "NVIDIA driver installation failed - please install manually: apt install nvidia-utils-${nvidia_major_version}" - fi + msg_error "NVIDIA libraries ${available_version} would not install" + local apt_reason="" reason_line + apt_reason="$("${apt_install[@]}" -s $nvidia_pkgs 2>&1 | grep -E 'Depends:|^E: ' | sed 's/^ *//' | head -3 || true)" + while IFS= read -r reason_line; do + if [[ -n "$reason_line" ]]; then msg_warn "apt: ${reason_line}"; fi + done <<<"$apt_reason" fi else - msg_warn "No NVIDIA packages for version ${nvidia_major_version}.x in CUDA repo" - # Fallback to Ubuntu repo packages with versioned nvidia-utils - msg_info "Trying Ubuntu native packages (nvidia-utils-${nvidia_major_version})" - if $STD apt-get -y -o Dpkg::Options::="--force-confold" install --no-install-recommends \ - libnvidia-decode-${nvidia_major_version} libnvidia-encode-${nvidia_major_version} nvidia-utils-${nvidia_major_version} 2>/dev/null; then - msg_ok "Installed Ubuntu NVIDIA packages (${nvidia_major_version})" - else - msg_warn "NVIDIA driver installation failed - please install manually: apt install nvidia-utils-${nvidia_major_version}" - fi + msg_warn "No NVIDIA ${nvidia_major_version}.x userspace libraries exist for ${os_id} ${os_version}" + msg_warn "Host driver is ${nvidia_host_version}. Either run a driver series the container OS carries, or pick a container OS that carries this one." fi fi diff --git a/tests/gpu-pci-class.sh b/tests/gpu-pci-class.sh index b584ef1d..847b9938 100644 --- a/tests/gpu-pci-class.sh +++ b/tests/gpu-pci-class.sh @@ -42,5 +42,18 @@ nn=( check "lspci -nn and -D: only display controllers" "0b:00.0 0000:0e:00.0" \ "$(printf '%s\n' "${nn[@]}" | grep -E "$PATTERN" | awk '{print $1}' | xargs)" +eval "$(awk '/^_gpu_type[(][)] [{]/,/^}/' "$CORE_DIR/lib/hwaccel.func")" +check "Battlemage by ID, named" INTEL_ARC "$(_gpu_type 8086:e20b 'Intel Corporation Battlemage G21 [Intel Graphics]')" +check "Battlemage by ID, unnamed" INTEL_ARC "$(_gpu_type 8086:e212 'Intel Corporation Device e212')" +check "Alchemist by ID" INTEL_ARC "$(_gpu_type 8086:56a0 'Intel Corporation Device 56a0')" +check "integrated Arc by name" INTEL_ARC "$(_gpu_type 8086:7d55 'Intel Corporation Meteor Lake-P [Intel Arc Graphics]')" +check "UHD is Gen9+" INTEL_GEN9+ "$(_gpu_type 8086:3e92 'Intel Corporation CoffeeLake-S GT2 [UHD Graphics 630]')" +check "HD 530 is Gen9+" INTEL_GEN9+ "$(_gpu_type 8086:1912 'Intel Corporation HD Graphics 530 (rev 06)')" +check "HD 4000 is legacy" INTEL_LEGACY "$(_gpu_type 8086:0162 'Intel Corporation IvyBridge GT2 [HD Graphics 4000]')" +check "an unknown Intel card stays generic" INTEL "$(_gpu_type 8086:a7a0 'Intel Corporation Device a7a0')" +check "NVIDIA by vendor, unnamed" NVIDIA "$(_gpu_type 10de:2484 'NVIDIA Corporation Device 2484')" +check "AMD by vendor" AMD "$(_gpu_type 1002:73bf 'Advanced Micro Devices, Inc. [AMD/ATI] Navi 21')" +check "another vendor is no GPU we set up" "" "$(_gpu_type 1a03:2000 'ASPEED Technology, Inc. ASPEED Graphics Family')" + printf '%s passed, %s failed\n' "$passed" "$failed" ((failed == 0)) diff --git a/tests/nvidia-version-pin.sh b/tests/nvidia-version-pin.sh index 39800432..eeb3f53f 100644 --- a/tests/nvidia-version-pin.sh +++ b/tests/nvidia-version-pin.sh @@ -19,9 +19,15 @@ check() { # Newest first, like madison. apt-cache() { - local v - for v in 620.70.06-1 595.112.07-1 595.71.05-1 590.48.01-1; do - printf ' libcuda1 | %s | https://developer.download.nvidia.com/compute/cuda/repos/debian13/x86_64 Packages\n' "$v" + local v list + case "$2" in + libcuda1) list="620.70.06-1 595.112.07-1 595.71.05-1 590.48.01-1" ;; + libnvidia-compute) list="620.70.06-1ubuntu1 595.112.07-1ubuntu1" ;; + libnvidia-compute-595) list="595.99.02-0ubuntu0.24.04.1" ;; + *) return 0 ;; + esac + for v in $list; do + printf ' %s | %s | https://developer.download.nvidia.com/compute/cuda/repos Packages\n' "$2" "$v" done } @@ -29,6 +35,8 @@ check "the host's exact release wins" 595.112.07-1 "$(_cuda_pick_version 595.112 check "a mid-series host is not handed a newer release" 595.71.05-1 "$(_cuda_pick_version 595.71.05)" check "a missing release falls back to the series" 595.112.07-1 "$(_cuda_pick_version 595.80.01)" check "another series is never taken" "" "$(_cuda_pick_version 580.126.09)" +check "Ubuntu: NVIDIA's package has the host's release" 595.112.07-1ubuntu1 "$(_cuda_pick_version 595.112.07 libnvidia-compute)" +check "Ubuntu: the archive's package only has its own" 595.99.02-0ubuntu0.24.04.1 "$(_cuda_pick_version 595.112.07 libnvidia-compute-595)" pins="$(_nvidia_apt_pins 595.112.07-1)" check "apt reads the version stanza first" "Pin: version 595.112.07-1" "$(grep -m1 '^Pin:' <<<"$pins")" @@ -50,7 +58,7 @@ covered() { } uncovered=() set -f -for pkg in libcuda1 libnvcuvid1 libnvidia-encode1 libnvidia-ml1 libnvidia-ptxjitcompiler1 libnvidia-gpucomp libnvidia-pkcs11-openssl3; do +for pkg in libcuda1 libnvcuvid1 libnvidia-encode1 libnvidia-ml1 libnvidia-ptxjitcompiler1 libnvidia-gpucomp libnvidia-pkcs11-openssl3 libnvidia-compute nvidia-persistenced; do covered "$pkg" || uncovered+=("$pkg") done set +f From 43f5c2d659b3b9d9ad78ecad8f7b24470d43408d Mon Sep 17 00:00:00 2001 From: MickLesk <47820557+MickLesk@users.noreply.github.com> Date: Fri, 13 Nov 2026 15:45:18 +0100 Subject: [PATCH 2/2] Trim the comments to what a reader would get wrong --- lib/hwaccel.func | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/hwaccel.func b/lib/hwaccel.func index 311afbfa..d076152e 100644 --- a/lib/hwaccel.func +++ b/lib/hwaccel.func @@ -19,8 +19,7 @@ _TOOLS_HWACCEL_LOADED=1 # single warning line. The batch is still tried first -- one apt run, and the # normal case -- and only a failure falls back to one at a time, which is also # the only way to name the package that was actually missing. -# Vendor and Intel's discrete cards by PCI ID ("8086:e20b"): the name depends -# on the pci.ids at hand, and a new card has none yet. +# Vendor, and Intel's discrete cards, by PCI ID: names depend on the local pci.ids. _gpu_type() { local id="$1" name="$2" case "${id%%:*}" in @@ -890,9 +889,7 @@ _setup_nvidia_gpu() { local nvidia_major_version="${nvidia_host_version%%.*}" - # Ubuntu has no libcuda1: libnvidia-compute carries it. NVIDIA names it with - # the series up to 580 and without from 590 on, and Ubuntu's own archive - # keeps the series name. The one that has the host's release wins. + # Ubuntu has no libcuda1 (libnvidia-compute); NVIDIA drops the series suffix from 590 on. local nv_suffix="" available_version="" cand cand_version for cand in "-${nvidia_major_version}" ""; do cand_version="$(_cuda_pick_version "$nvidia_host_version" "libnvidia-compute${cand}")"