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