From b5476c75fa6b3e64de79ee8a82384fe668abe409 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 17 Aug 2026 12:05:40 +0200 Subject: [PATCH] gpu: nvidia install drops VFIO autoload; GPU flows self-repair stale vfio.conf The nvidia driver installer now writes only `nvidia` and `nvidia_uvm` into `/etc/modules-load.d/nvidia-vfio.conf`, leaving the vfio-pci modules for `switch_gpu_mode.sh` to add and remove when the user explicitly transitions between LXC and VM mode. A host with residual `/etc/modprobe.d/vfio.conf` entries from an earlier VM passthrough setup no longer has vfio-pci racing nvidia at boot and capturing the GPU by ID. All three GPU-facing entry points also detect a stale `vfio.conf` entry whose vid:did belongs to a GPU currently bound to a non-vfio driver, and offer to clean it in place. `nvidia_installer.sh` runs the check as a pre-flight before install. `switch_gpu_mode.sh` presents an interactive dialog and rebuilds initramfs. `switch_gpu_mode_direct.sh` performs the same cleanup transparently for the Monitor web variant. Once cleaned, "Add GPU to LXC", the driver install and Switch Mode all proceed without any manual intervention. The two supporting helpers `_pci_driver_of` and `_clean_vfio_conf_ids` previously local to `add_gpu_lxc.sh` move to `scripts/global/pci_passthrough_helpers.sh`, so every GPU-related script resolves them from a single source without duplication. --- scripts/global/pci_passthrough_helpers.sh | 56 +++++++++++++++++++++ scripts/gpu_tpu/add_gpu_lxc.sh | 57 --------------------- scripts/gpu_tpu/nvidia_installer.sh | 60 +++++++++++++++++++++-- scripts/gpu_tpu/switch_gpu_mode.sh | 51 +++++++++++++++++++ scripts/gpu_tpu/switch_gpu_mode_direct.sh | 37 ++++++++++++++ 5 files changed, 199 insertions(+), 62 deletions(-) diff --git a/scripts/global/pci_passthrough_helpers.sh b/scripts/global/pci_passthrough_helpers.sh index 9a3fec77..f501def1 100644 --- a/scripts/global/pci_passthrough_helpers.sh +++ b/scripts/global/pci_passthrough_helpers.sh @@ -669,3 +669,59 @@ _proxmenux_nvidia_migrate_legacy_blacklist() { fi fi } + +# Return the kernel driver bound to a PCI slot, or empty if none. +_pci_driver_of() { + local pci="$1" + [[ -z "$pci" ]] && return + local pci_full="$pci" + [[ "$pci_full" != 0000:* ]] && pci_full="0000:${pci_full}" + local link="/sys/bus/pci/devices/${pci_full}/driver" + [[ -L "$link" ]] && basename "$(readlink "$link")" +} + +# Remove one or more `vid:did` tokens from the `ids=` list in +# /etc/modprobe.d/vfio.conf. Preserves any remaining tokens and any +# trailing options on the line. Returns 0 when the file changes. +_clean_vfio_conf_ids() { + local vfio_conf="/etc/modprobe.d/vfio.conf" + [[ ! -f "$vfio_conf" ]] && return 1 + local -a targets=("$@") + [[ ${#targets[@]} -eq 0 ]] && return 1 + local before after tmp + before=$(cat "$vfio_conf") + tmp=$(mktemp) + awk -v targets="${targets[*]}" ' + BEGIN { + n = split(targets, a, " ") + for (i = 1; i <= n; i++) drop[a[i]] = 1 + } + /^options vfio-pci ids=/ { + pre = ""; ids = ""; post = "" + match($0, /ids=[^ \t]+/) + pre = substr($0, 1, RSTART - 1) + idsp = substr($0, RSTART, RLENGTH) + post = substr($0, RSTART + RLENGTH) + sub(/^ids=/, "", idsp) + m = split(idsp, tok, ",") + out = "" + for (i = 1; i <= m; i++) { + t = tok[i] + if (!(t in drop)) { + out = (out == "" ? t : out "," t) + } + } + if (out == "") next + print pre "ids=" out post + next + } + { print } + ' "$vfio_conf" > "$tmp" + after=$(cat "$tmp") + if [[ "$before" == "$after" ]]; then + rm -f "$tmp" + return 1 + fi + mv "$tmp" "$vfio_conf" + return 0 +} diff --git a/scripts/gpu_tpu/add_gpu_lxc.sh b/scripts/gpu_tpu/add_gpu_lxc.sh index 38554199..ffcf609c 100644 --- a/scripts/gpu_tpu/add_gpu_lxc.sh +++ b/scripts/gpu_tpu/add_gpu_lxc.sh @@ -930,63 +930,6 @@ _remove_vfio_modules() { sed -i '/^vfio_virqfd$/d' "$modules_file" } -# Return the kernel driver bound to a PCI slot, or empty if none. -_pci_driver_of() { - local pci="$1" - [[ -z "$pci" ]] && return - local pci_full="$pci" - [[ "$pci_full" != 0000:* ]] && pci_full="0000:${pci_full}" - local link="/sys/bus/pci/devices/${pci_full}/driver" - [[ -L "$link" ]] && basename "$(readlink "$link")" -} - -# Remove one or more `vid:did` tokens from the `ids=` list in -# /etc/modprobe.d/vfio.conf. Preserves any remaining tokens and any -# trailing options on the line. Returns 0 when the file changes. -_clean_vfio_conf_ids() { - local vfio_conf="/etc/modprobe.d/vfio.conf" - [[ ! -f "$vfio_conf" ]] && return 1 - local -a targets=("$@") - [[ ${#targets[@]} -eq 0 ]] && return 1 - local before after tmp - before=$(cat "$vfio_conf") - tmp=$(mktemp) - awk -v targets="${targets[*]}" ' - BEGIN { - n = split(targets, a, " ") - for (i = 1; i <= n; i++) drop[a[i]] = 1 - } - /^options vfio-pci ids=/ { - # Split "options vfio-pci ids=A,B,C key=val …" preserving order. - pre = ""; ids = ""; post = "" - match($0, /ids=[^ \t]+/) - pre = substr($0, 1, RSTART - 1) - idsp = substr($0, RSTART, RLENGTH) - post = substr($0, RSTART + RLENGTH) - sub(/^ids=/, "", idsp) - m = split(idsp, tok, ",") - out = "" - for (i = 1; i <= m; i++) { - t = tok[i] - if (!(t in drop)) { - out = (out == "" ? t : out "," t) - } - } - if (out == "") next # drop the whole line if no ids left - print pre "ids=" out post - next - } - { print } - ' "$vfio_conf" > "$tmp" - after=$(cat "$tmp") - if [[ "$before" == "$after" ]]; then - rm -f "$tmp" - return 1 - fi - mv "$tmp" "$vfio_conf" - return 0 -} - # Return live VFIO state for each selected GPU: # sets ACTIVE_VFIO_* arrays for GPUs whose PCI slot is currently # bound to vfio-pci (real VFIO passthrough right now). diff --git a/scripts/gpu_tpu/nvidia_installer.sh b/scripts/gpu_tpu/nvidia_installer.sh index 03df5593..2aec88fb 100644 --- a/scripts/gpu_tpu/nvidia_installer.sh +++ b/scripts/gpu_tpu/nvidia_installer.sh @@ -49,6 +49,11 @@ export COMPONENTS_STATUS_FILE if [[ -f "$UTILS_FILE" ]]; then source "$UTILS_FILE" fi +if [[ -f "$LOCAL_SCRIPTS/global/pci_passthrough_helpers.sh" ]]; then + source "$LOCAL_SCRIPTS/global/pci_passthrough_helpers.sh" +elif [[ -f "$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)/global/pci_passthrough_helpers.sh" ]]; then + source "$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd)/global/pci_passthrough_helpers.sh" +fi if [[ ! -f "$COMPONENTS_STATUS_FILE" ]]; then echo "{}" > "$COMPONENTS_STATUS_FILE" @@ -124,6 +129,54 @@ check_gpu_not_in_vm_passthrough() { exit 0 } +check_stale_vfio_config_for_nvidia() { + local vfio_conf="/etc/modprobe.d/vfio.conf" + [[ ! -f "$vfio_conf" ]] && return 0 + + local ids_line ids_part + ids_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1) + [[ -z "$ids_line" ]] && return 0 + ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//') + [[ -z "$ids_part" ]] && return 0 + + local dev vendor did vid_did + local -a legacy_ids=() + local legacy_list="" + + for dev in /sys/bus/pci/devices/*; do + vendor=$(cat "$dev/vendor" 2>/dev/null) + [[ "$vendor" != "0x10de" ]] && continue + did=$(cat "$dev/device" 2>/dev/null) + [[ -z "$did" ]] && continue + vid_did="10de:${did#0x}" + if echo ",${ids_part}," | grep -q ",${vid_did},"; then + legacy_ids+=("$vid_did") + legacy_list+=" • $(basename "$dev") [${vid_did}]\n" + fi + done + + [[ ${#legacy_ids[@]} -eq 0 ]] && return 0 + + local msg + msg="\n$(translate 'A previous VFIO passthrough configuration was detected for the following NVIDIA GPU(s):')\n\n" + msg+="${legacy_list}\n" + msg+="$(translate 'The active kernel driver is not vfio-pci, but the entry in') /etc/modprobe.d/vfio.conf $(translate 'will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.')\n\n" + msg+="\Z1\Zb$(translate 'Do you want to remove the stale entry from vfio.conf and continue?')\Zn" + + dialog --colors --backtitle "ProxMenux" \ + --title "$(translate 'Stale VFIO Config Detected')" \ + --yesno "$msg" 18 78 || exit 0 + + if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \ + && _clean_vfio_conf_ids "${legacy_ids[@]}"; then + msg_info "$(translate 'Rebuilding initramfs after vfio.conf cleanup...')" + update-initramfs -u >/dev/null 2>&1 || true + msg_ok "$(translate 'Stale VFIO entries removed and initramfs rebuilt.')" | tee -a "$screen_capture" + else + msg_ok "$(translate 'No changes were needed in vfio.conf.')" | tee -a "$screen_capture" + fi +} + detect_driver_status() { CURRENT_DRIVER_INSTALLED=false CURRENT_DRIVER_VERSION="" @@ -531,12 +584,8 @@ EOF } ensure_modules_config() { - msg_info "$(translate 'Configuring NVIDIA and VFIO modules...')" + msg_info "$(translate 'Configuring NVIDIA modules...')" cat > /etc/modules-load.d/nvidia-vfio.conf <<'EOF' -vfio -vfio_iommu_type1 -vfio_pci -vfio_virqfd nvidia nvidia_uvm EOF @@ -1676,6 +1725,7 @@ main() { detect_nvidia_gpus detect_driver_status check_gpu_not_in_vm_passthrough + check_stale_vfio_config_for_nvidia if ! $NVIDIA_GPU_PRESENT; then dialog --backtitle "ProxMenux" --title "$(translate 'NVIDIA GPU Driver Installation')" --msgbox \ diff --git a/scripts/gpu_tpu/switch_gpu_mode.sh b/scripts/gpu_tpu/switch_gpu_mode.sh index 02acb793..1c2aeab0 100644 --- a/scripts/gpu_tpu/switch_gpu_mode.sh +++ b/scripts/gpu_tpu/switch_gpu_mode.sh @@ -563,8 +563,59 @@ _selected_gpu_current_mode() { echo "$mode" } +check_stale_vfio_config_switch_mode() { + local vfio_conf="/etc/modprobe.d/vfio.conf" + [[ ! -f "$vfio_conf" ]] && return 0 + + local ids_line ids_part + ids_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1) + [[ -z "$ids_line" ]] && return 0 + ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//') + [[ -z "$ids_part" ]] && return 0 + + local -a legacy_ids=() + local legacy_list="" + local idx viddid drv pci name + for idx in "${SELECTED_GPU_IDX[@]}"; do + viddid="${ALL_GPU_VIDDID[$idx]}" + drv="${ALL_GPU_DRIVERS[$idx]}" + pci="${ALL_GPU_PCIS[$idx]}" + name="${ALL_GPU_NAMES[$idx]}" + [[ -z "$viddid" ]] && continue + [[ "$drv" == "vfio-pci" ]] && continue + if echo ",${ids_part}," | grep -q ",${viddid},"; then + legacy_ids+=("$viddid") + legacy_list+=" • ${name} (${pci}) [${viddid}]\n" + fi + done + + [[ ${#legacy_ids[@]} -eq 0 ]] && return 0 + + local msg + msg="\n$(translate 'The following selected GPU(s) still have a VFIO passthrough entry in') /etc/modprobe.d/vfio.conf:\n\n" + msg+="${legacy_list}\n" + msg+="$(translate 'The active kernel driver is not vfio-pci, but the entry will rebind the GPU to vfio-pci on the next reboot.')\n\n" + msg+="\Z1\Zb$(translate 'Do you want to remove the stale entry from vfio.conf?')\Zn" + + dialog --colors --backtitle "ProxMenux" \ + --title "$(translate 'Stale VFIO Config Detected')" \ + --yesno "$msg" 18 80 || return 0 + + if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \ + && _clean_vfio_conf_ids "${legacy_ids[@]}"; then + msg_info "$(translate 'Rebuilding initramfs after vfio.conf cleanup...')" + update-initramfs -u >/dev/null 2>&1 || true + dialog --backtitle "ProxMenux" \ + --title "$(translate 'Stale VFIO Config Cleaned')" \ + --msgbox "\n$(translate 'The vfio.conf entries have been removed and initramfs rebuilt.')\n\n$(translate 'A reboot is recommended before the GPU is guaranteed to stay on the native driver.')" \ + 12 78 + exit 0 + fi +} + select_target_mode() { CURRENT_MODE=$(_selected_gpu_current_mode) + check_stale_vfio_config_switch_mode if [[ "$CURRENT_MODE" == "mixed" ]]; then local msg idx mode_label diff --git a/scripts/gpu_tpu/switch_gpu_mode_direct.sh b/scripts/gpu_tpu/switch_gpu_mode_direct.sh index cd14b9b7..746f61be 100644 --- a/scripts/gpu_tpu/switch_gpu_mode_direct.sh +++ b/scripts/gpu_tpu/switch_gpu_mode_direct.sh @@ -1257,6 +1257,39 @@ parse_arguments() { # ========================================================== # Main Entry Point # ========================================================== +check_stale_vfio_config_switch_mode_direct() { + local vfio_conf="/etc/modprobe.d/vfio.conf" + [[ ! -f "$vfio_conf" ]] && return 0 + + local ids_line ids_part + ids_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1) + [[ -z "$ids_line" ]] && return 0 + ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//') + [[ -z "$ids_part" ]] && return 0 + + local -a legacy_ids=() + local idx viddid drv + for idx in "${SELECTED_GPU_IDX[@]}"; do + viddid="${ALL_GPU_VIDDID[$idx]}" + drv="${ALL_GPU_DRIVERS[$idx]}" + [[ -z "$viddid" ]] && continue + [[ "$drv" == "vfio-pci" ]] && continue + if echo ",${ids_part}," | grep -q ",${viddid},"; then + legacy_ids+=("$viddid") + fi + done + + [[ ${#legacy_ids[@]} -eq 0 ]] && return 0 + + msg_info "$(translate 'Removing stale VFIO entries from vfio.conf...')" + if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \ + && _clean_vfio_conf_ids "${legacy_ids[@]}"; then + update-initramfs -u >/dev/null 2>&1 || true + HOST_CONFIG_CHANGED=true + msg_ok "$(translate 'Stale VFIO entries removed and initramfs rebuilt.')" | tee -a "$screen_capture" + fi +} + main() { : >"$LOG_FILE" : >"$screen_capture" @@ -1323,6 +1356,10 @@ main() { local gpu_name="${ALL_GPU_NAMES[$gpu_idx]}" local gpu_pci="${ALL_GPU_PCIS[$gpu_idx]}" + if [[ "$TARGET_MODE" == "lxc" ]]; then + check_stale_vfio_config_switch_mode_direct + fi + # Execute the switch if [[ "$TARGET_MODE" == "vm" ]]; then switch_to_vm_mode