refine post-install and hardware GPU docs, Monitor UX and CLI styling

- rewrite the 15 post-install pages and the 3 hardware GPU pages so they reflect the current scripts (reversibility, tracked-tool counts, kernel parameters, per-tool commands, Alpine LXC propagation flow)
- migrate the legacy step-badge helper on post-install/optional and create-vm/synology to the canonical pill component, with the stepLabel key added in each locale
- fix rich-text i18n calls missing helpers across network, automated, optional, security, customization and the post-install landing pages, and escape the `<iface>` placeholder in automated so intl no longer parses it as a tag
- remove the mouse-follow blue overlay from the docs landing layout
- reposition the App-tab Edit button and stack the Search and Register controls vertically on mobile
- move the Bulk update Configure/Edit control into the section header so it behaves the same on desktop and mobile
- show a spinner during the final autoremove/autoclean pass of update-pve-safe so the cleanup step reads as active instead of silent
- restyle the shell spinner and msg_info in a distinctive purple and drop the unused msg_lang duplicate
- add a web-docs i18n build script and its CI workflow, plus tests for the pushover notification channel
This commit is contained in:
MacRimi
2026-08-26 17:23:09 +02:00
parent b71dd65898
commit fcfe8da765
106 changed files with 3376 additions and 1358 deletions
+50 -110
View File
@@ -5,8 +5,8 @@
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : GPL-3.0
# Version : 1.0
# Last Updated: 03/04/2026
# Version : 1.1
# Last Updated: 26/08/2026
# ==========================================================
# Description:
# Automates full GPU passthrough (VFIO) from Proxmox host to a VM.
@@ -323,25 +323,40 @@ evaluate_host_reboot_requirement() {
_file_has_exact_line "$mod" "$modules_file" || needs_change=true
done
# vfio-pci ids
# VFIO ownership. NVIDIA uses exact BDFs so another GPU with the same
# vendor:device ID can remain native; AMD/Intel keep the legacy IDs list.
local vfio_conf="/etc/modprobe.d/vfio.conf"
local ids_line ids_part
ids_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
if [[ -z "$ids_line" ]]; then
needs_change=true
else
[[ "$ids_line" == *"disable_vga=1"* ]] || needs_change=true
ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//')
local existing_ids=()
IFS=',' read -ra existing_ids <<< "$ids_part"
local required found existing
for required in "${IOMMU_VFIO_IDS[@]}"; do
found=false
for existing in "${existing_ids[@]}"; do
[[ "$existing" == "$required" ]] && found=true && break
done
$found || needs_change=true
if [[ "$SELECTED_GPU" == "nvidia" ]]; then
local required_bdf
for required_bdf in "${IOMMU_DEVICES[@]}"; do
if ! declare -F _proxmenux_vfio_bind_has_bdf >/dev/null 2>&1 \
|| ! _proxmenux_vfio_bind_has_bdf "$required_bdf"; then
needs_change=true
fi
done
_file_has_exact_line "softdep nvidia pre: vfio-pci" "$vfio_conf" || needs_change=true
_file_has_exact_line "softdep nvidia_drm pre: vfio-pci" "$vfio_conf" || needs_change=true
_file_has_exact_line "softdep nvidia_modeset pre: vfio-pci" "$vfio_conf" || needs_change=true
_file_has_exact_line "softdep nvidia_uvm pre: vfio-pci" "$vfio_conf" || needs_change=true
else
ids_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
if [[ -z "$ids_line" ]]; then
needs_change=true
else
[[ "$ids_line" == *"disable_vga=1"* ]] || needs_change=true
ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//')
local existing_ids=()
IFS=',' read -ra existing_ids <<< "$ids_part"
local required found existing
for required in "${IOMMU_VFIO_IDS[@]}"; do
found=false
for existing in "${existing_ids[@]}"; do
[[ "$existing" == "$required" ]] && found=true && break
done
$found || needs_change=true
done
fi
fi
# modprobe options files
@@ -362,21 +377,16 @@ evaluate_host_reboot_requirement() {
case "$SELECTED_GPU" in
nvidia)
_file_has_exact_line "blacklist nouveau" "$blacklist_file" || needs_change=true
_file_has_exact_line "blacklist nvidia" "$blacklist_file" || needs_change=true
_file_has_exact_line "blacklist nvidia_drm" "$blacklist_file" || needs_change=true
_file_has_exact_line "blacklist nvidia_modeset" "$blacklist_file" || needs_change=true
_file_has_exact_line "blacklist nvidia_uvm" "$blacklist_file" || needs_change=true
_file_has_exact_line "blacklist nvidiafb" "$blacklist_file" || needs_change=true
_file_has_exact_line "blacklist lbm-nouveau" "$blacklist_file" || needs_change=true
_file_has_exact_line "options nouveau modeset=0" "$blacklist_file" || needs_change=true
[[ -f /etc/modules-load.d/nvidia-vfio.conf ]] && needs_change=true
grep -qE '^(nvidia|nvidia_uvm|nvidia_drm|nvidia_modeset)$' /etc/modules 2>/dev/null && needs_change=true
local svc
for svc in nvidia-persistenced.service nvidia-persistenced nvidia-powerd.service nvidia-fabricmanager.service; do
if systemctl is-active --quiet "$svc" 2>/dev/null || systemctl is-enabled --quiet "$svc" 2>/dev/null; then
needs_change=true
fi
done
# The managed global NVIDIA blacklist is required only when
# every NVIDIA GPU is in VFIO. On a mixed host it must be absent.
if declare -F _proxmenux_all_nvidia_in_vfio >/dev/null 2>&1 \
&& _proxmenux_all_nvidia_in_vfio; then
[[ -f /etc/modprobe.d/proxmenux-nvidia-vfio-blacklist.conf ]] || needs_change=true
else
[[ -f /etc/modprobe.d/proxmenux-nvidia-vfio-blacklist.conf ]] && needs_change=true
fi
;;
amd)
_file_has_exact_line "blacklist radeon" "$blacklist_file" || needs_change=true
@@ -1611,8 +1621,8 @@ configure_vfio_pci_ids() {
# NVIDIA: per-BDF binding (multi-GPU safe). The `options vfio-pci
# ids=VENDOR:DEVICE` approach captures EVERY GPU with the same
# vendor:device ID — fatal when two NVIDIA GPUs share a model.
# Instead, we list the exact BDF(s) of the target GPU in the
# initramfs hook, and add `softdep nvidia pre: vfio-pci` so vfio
# Instead, we list the exact BDF(s) of the target GPU in an early
# udev driver_override rule, and add `softdep nvidia pre: vfio-pci` so vfio
# has a chance to claim the BDF before nvidia loads.
# ────────────────────────────────────────────────────────────────
if [[ "$SELECTED_GPU" == "nvidia" ]]; then
@@ -1649,7 +1659,7 @@ configure_vfio_pci_ids() {
_add_line_if_missing "softdep nvidia_modeset pre: vfio-pci" "$vfio_conf"
_add_line_if_missing "softdep nvidia_uvm pre: vfio-pci" "$vfio_conf"
# Per-BDF binder hook. IOMMU_DEVICES has the BDFs for the GPU
# Per-BDF binder rule. IOMMU_DEVICES has the BDFs for the GPU
# we're passing (and any same-group functions like the audio
# function). Add all of them so the whole IOMMU group goes to
# vfio-pci as Proxmox expects.
@@ -1755,85 +1765,15 @@ blacklist_gpu_drivers() {
}
sanitize_nvidia_host_stack_for_vfio() {
# In the new per-BDF model we only stop systemd services that could
# actively probe / lock GPUs at boot (persistenced) — but we DO NOT:
# - blacklist the nvidia kernel module
# - remove nvidia entries from /etc/modules
# - rename /etc/modules-load.d/nvidia-vfio.conf
# - rename /etc/udev/rules.d/70-nvidia.rules
# - create /etc/modprobe.d/nvidia-blacklist.conf with install /bin/false
# All of those were global and broke multi-GPU NVIDIA scenarios where
# one GPU goes to a VM (vfio-pci) and another stays on the host
# (nvidia driver). VFIO binding is now per-BDF via driver_override in
# an initramfs hook — the nvidia module stays usable for any GPU not
# explicitly targeted.
# Host-wide NVIDIA services and module blacklisting are derived from
# the complete per-BDF state. With two NVIDIA GPUs, assigning only one
# to a VM keeps the native driver and services available for the other.
msg_info "$(translate 'Sanitizing NVIDIA host services for VFIO mode...')"
local changed=false
local state_dir="/var/lib/proxmenux"
local state_file="${state_dir}/nvidia-host-services.state"
local svc
local -a services=(
"nvidia-persistenced.service"
"nvidia-powerd.service"
"nvidia-fabricmanager.service"
)
mkdir -p "$state_dir" >/dev/null 2>&1 || true
: > "$state_file"
for svc in "${services[@]}"; do
local was_enabled=0 was_active=0
if systemctl is-enabled --quiet "$svc" 2>/dev/null; then
was_enabled=1
fi
if systemctl is-active --quiet "$svc" 2>/dev/null; then
was_active=1
fi
if (( was_enabled == 1 || was_active == 1 )); then
echo "${svc} enabled=${was_enabled} active=${was_active}" >>"$state_file"
fi
if systemctl is-active --quiet "$svc" 2>/dev/null; then
systemctl stop "$svc" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
if systemctl is-enabled --quiet "$svc" 2>/dev/null; then
systemctl disable "$svc" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
done
[[ -s "$state_file" ]] || rm -f "$state_file"
if $changed; then
HOST_CONFIG_CHANGED=true
_proxmenux_nvidia_vfio_policy_sync || true
if _proxmenux_all_nvidia_in_vfio; then
msg_ok "$(translate 'NVIDIA host services disabled for VFIO mode')" | tee -a "$screen_capture"
else
msg_ok "$(translate 'NVIDIA host services already aligned for VFIO mode')" | tee -a "$screen_capture"
fi
# Sync components_status.json — the host driver stays on disk but is
# not in use for this GPU because it now belongs to a VM. Per-BDF
# model: on multi-GPU hosts where another NVIDIA card still uses the
# nvidia driver, keep the status as "installed" — the driver is
# genuinely in use elsewhere. Only flip to "vfio_passthrough" when no
# NVIDIA GPU is bound to the host driver anymore.
if declare -F update_component_status >/dev/null 2>&1; then
local _nvd_ver _nvd_new_status
_nvd_ver=$(jq -r '.nvidia_driver.version // ""' \
/usr/local/share/proxmenux/components_status.json 2>/dev/null)
_nvd_new_status="vfio_passthrough"
# Any NVIDIA PCI device still using the nvidia driver on the host?
if lspci -nnk 2>/dev/null | awk '
/NVIDIA/{gpu=1; next}
gpu && /Kernel driver in use: nvidia$/ {found=1; exit}
/^[^\t]/{gpu=0}
END{exit !found}
'; then
_nvd_new_status="installed"
fi
update_component_status "nvidia_driver" "$_nvd_new_status" \
"${_nvd_ver:-}" "gpu" '{"patched":false}' >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'NVIDIA host services/autoload already aligned for native mode')" | tee -a "$screen_capture"
fi
}
+72 -44
View File
@@ -5,18 +5,18 @@
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : GPL-3.0
# Version : 1.2
# Last Updated: 26/03/2026
# Version : 1.3
# Last Updated: 26/08/2026
# ==========================================================
# Description:
# Installs and manages the NVIDIA proprietary driver on a
# Proxmox VE host. Detects hardware, picks a kernel-compatible
# driver version and handles the full lifecycle
# Proxmox VE host. Detects hardware, filters NVIDIA branches by
# the installed GPU PCI IDs and handles the full lifecycle
# (install / update / remove).
#
# Features:
# - GPU detection + VFIO passthrough safety check
# - Kernel-aware driver version filter (5.15 → 6.17+)
# - GPU PCI-ID-aware branch filtering from NVIDIA supportedchips
# - Nouveau blacklist + module unload
# - DKMS-backed install (survives kernel upgrades)
# - udev rules + nvidia-persistenced service
@@ -36,6 +36,10 @@ screen_capture="/tmp/proxmenux_nvidia_screen_capture_$$.txt"
NVIDIA_BASE_URL="https://download.nvidia.com/XFree86/Linux-x86_64"
NVIDIA_WORKDIR="/opt/nvidia"
NVIDIA_NOUVEAU_BLACKLIST="/etc/modprobe.d/proxmenux-nouveau-blacklist.conf"
NVIDIA_NOUVEAU_STATE="${BASE_DIR}/nvidia-nouveau-blacklist.state"
NVIDIA_NOUVEAU_LEGACY_BLACKLIST="/etc/modprobe.d/nouveau-blacklist.conf"
NVIDIA_GLOBAL_BLACKLIST="/etc/modprobe.d/blacklist.conf"
# LXC post-install update constants (used only when NVIDIA LXC passthrough
# containers are detected and the user confirms updating them after the host
@@ -541,16 +545,65 @@ ensure_repos_and_headers() {
msg_ok "$(translate 'Kernel headers and build tools verified.')" | tee -a "$screen_capture"
}
_nouveau_legacy_file_is_proxmenux_shape() {
[[ -f "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST" ]] || return 1
local content
content=$(sed '/^[[:space:]]*$/d' "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST" 2>/dev/null)
[[ "$content" == $'blacklist nouveau\noptions nouveau modeset=0' ]]
}
_nouveau_state_set() {
local key="$1"
mkdir -p "$(dirname "$NVIDIA_NOUVEAU_STATE")"
touch "$NVIDIA_NOUVEAU_STATE"
grep -qFx "${key}=1" "$NVIDIA_NOUVEAU_STATE" 2>/dev/null \
|| echo "${key}=1" >> "$NVIDIA_NOUVEAU_STATE"
}
restore_nouveau_after_uninstall() {
local remove_global_line=false
if [[ -f "$NVIDIA_NOUVEAU_STATE" ]] \
&& grep -qFx 'blacklist_conf_line_added=1' "$NVIDIA_NOUVEAU_STATE" 2>/dev/null; then
remove_global_line=true
fi
# Migration for installations made by older ProxMenux versions. That
# version overwrote this exact two-line file and added the matching line
# to blacklist.conf, but had no ownership state yet.
if _nouveau_legacy_file_is_proxmenux_shape; then
rm -f "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST"
remove_global_line=true
fi
rm -f "$NVIDIA_NOUVEAU_BLACKLIST"
if $remove_global_line && [[ -f "$NVIDIA_GLOBAL_BLACKLIST" ]]; then
sed -i '/^blacklist nouveau$/d' "$NVIDIA_GLOBAL_BLACKLIST"
fi
rm -f "$NVIDIA_NOUVEAU_STATE"
}
blacklist_nouveau() {
msg_info "$(translate 'Blacklisting nouveau driver...')"
# Write blacklist config files
if ! grep -q '^blacklist nouveau' /etc/modprobe.d/blacklist.conf 2>/dev/null; then
echo "blacklist nouveau" >> /etc/modprobe.d/blacklist.conf
local legacy_owned=false
if _nouveau_legacy_file_is_proxmenux_shape; then
rm -f "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST"
legacy_owned=true
_nouveau_state_set "legacy_migrated"
fi
# Also write explicit options file to ensure it's fully disabled
cat > /etc/modprobe.d/nouveau-blacklist.conf <<'EOF'
if ! grep -q '^blacklist nouveau$' "$NVIDIA_GLOBAL_BLACKLIST" 2>/dev/null; then
echo "blacklist nouveau" >> "$NVIDIA_GLOBAL_BLACKLIST"
_nouveau_state_set "blacklist_conf_line_added"
elif $legacy_owned; then
# The legacy ProxMenux file proves ownership of the companion line.
_nouveau_state_set "blacklist_conf_line_added"
fi
# ProxMenux-owned file: uninstall can now remove only what we created.
cat > "$NVIDIA_NOUVEAU_BLACKLIST" <<'EOF'
# Managed by ProxMenux NVIDIA installer.
blacklist nouveau
options nouveau modeset=0
EOF
@@ -678,6 +731,7 @@ complete_nvidia_uninstall() {
rm -f /etc/udev/rules.d/70-nvidia.rules
rm -rf /usr/lib/modprobe.d/nvidia*.conf
rm -rf /etc/modprobe.d/nvidia*.conf
restore_nouveau_after_uninstall
if [[ -d "$NVIDIA_WORKDIR" ]]; then
find "$NVIDIA_WORKDIR" -type d -name "nvidia-persistenced" -exec rm -rf {} + 2>/dev/null || true
@@ -709,28 +763,14 @@ ensure_workdir() {
}
# ==========================================================
# Kernel + system detection
# System detection
# ==========================================================
get_kernel_compatibility_info() {
local kernel_version
kernel_version=$(uname -r)
get_system_info() {
if [[ -f /etc/pve/.version ]]; then
PVE_VERSION=$(cat /etc/pve/.version)
else
PVE_VERSION="unknown"
fi
KERNEL_MAJOR=$(echo "$kernel_version" | cut -d. -f1)
KERNEL_MINOR=$(echo "$kernel_version" | cut -d. -f2)
MIN_DRIVER_VERSION=""
RECOMMENDED_BRANCH=""
COMPATIBILITY_NOTE=""
}
is_version_compatible() {
return 0
}
@@ -1546,7 +1586,7 @@ show_version_menu() {
show_proxmenux_logo
msg_title "$(translate 'NVIDIA GPU Driver Installation')"
msg_info "$(translate 'Fetching compatible driver versions for your kernel and GPU...')"
msg_info "$(translate 'Fetching NVIDIA driver versions supported by your GPU...')"
latest=$(download_latest_version 2>/dev/null)
versions_list=$(list_available_versions 2>/dev/null)
@@ -1573,18 +1613,6 @@ show_version_menu() {
latest=$(echo "$latest" | tr -d '[:space:]')
local current_list="$versions_list"
# Apply kernel compatibility filter if needed
if [[ -n "$MIN_DRIVER_VERSION" ]]; then
local filtered_list=""
while IFS= read -r ver; do
[[ -z "$ver" ]] && continue
if is_version_compatible "$ver"; then
filtered_list+="$ver"$'\n'
fi
done <<< "$current_list"
current_list="$filtered_list"
fi
if [[ -n "$current_list" ]]; then
current_list=$(filter_option_c_branch "$current_list" "$CURRENT_DRIVER_VERSION" "")
@@ -1636,7 +1664,7 @@ show_version_menu() {
# 2. Fresh install (no current driver) → Production Branch head
# from NVIDIA's Unix drivers page, when present in the list.
# 3. Fallback → highest numeric in the list (Production may have
# been filtered out by kernel-compat / GPU-compat / patch
# been filtered out by maintained-branch / GPU PCI-ID / patch
# awareness).
latest=""
if [[ -n "$CURRENT_DRIVER_VERSION" && -n "$current_list" ]]; then
@@ -1665,7 +1693,7 @@ show_version_menu() {
fi
local menu_text="$(translate 'Select the NVIDIA driver version to install:')\n\n"
menu_text+="$(translate 'Versions shown are compatible with your kernel and your GPU. The recommended version keeps you on your current driver branch, or defaults to the NVIDIA Production Branch head on a fresh install.')"
menu_text+="$(translate 'Versions shown belong to maintained NVIDIA branches that list your GPU PCI ID. DKMS compilation is the final validation against the running kernel. The recommended version keeps the current branch, or uses the NVIDIA Production Branch on a fresh install.')"
if $patch_filtered; then
menu_text+="\n\n$(translate 'NVENC patch detected — list narrowed to versions supported by keylase/nvidia-patch.')"
elif [[ -n "$patch_filter_note" ]]; then
@@ -1689,7 +1717,7 @@ show_version_menu() {
choices+=("$ver" "$ver")
done <<< "$current_list"
else
choices+=("" "$(translate 'No compatible versions found for your kernel')")
choices+=("" "$(translate 'No supported NVIDIA versions found for this GPU')")
fi
stop_spinner
@@ -1741,7 +1769,7 @@ main() {
exit 0
fi
get_kernel_compatibility_info
get_system_info
show_version_menu
if [[ "$DRIVER_VERSION" == "cancel" || -z "$DRIVER_VERSION" ]]; then
@@ -2008,4 +2036,4 @@ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
exit $?
fi
main
fi
fi
+100 -208
View File
@@ -5,8 +5,8 @@
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : GPL-3.0
# Version : 1.0
# Last Updated: 05/04/2026
# Version : 1.1
# Last Updated: 26/08/2026
# ==========================================================
# Description:
# Moves an already-assigned GPU between the two modes it can
@@ -67,7 +67,8 @@ if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" ]]; then
elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh"
else
msg_warn "$(translate 'pci_passthrough_helpers.sh missing — SR-IOV / orphan-audio guards will be skipped')"
echo "ProxMenux: pci_passthrough_helpers.sh is required; refusing to change GPU ownership." >&2
exit 1
fi
load_language
initialize_cache
@@ -81,6 +82,8 @@ declare -a SELECTED_GPU_IDX=()
declare -a SELECTED_IOMMU_IDS=()
declare -a SELECTED_PCI_SLOTS=()
declare -a SELECTED_NVIDIA_BDFS=()
declare -a SELECTED_LEGACY_IOMMU_IDS=()
declare -a LXC_AFFECTED_CTIDS=()
declare -a LXC_AFFECTED_NAMES=()
@@ -167,12 +170,30 @@ _get_iommu_group_ids() {
done
}
_get_iommu_group_bdfs() {
local pci_full="$1"
local group_link="/sys/bus/pci/devices/${pci_full}/iommu_group"
[[ -L "$group_link" ]] || return 0
local group_dir dev_path dev_class
group_dir="/sys/kernel/iommu_groups/$(basename "$(readlink "$group_link")")/devices"
for dev_path in "${group_dir}/"*; do
[[ -e "$dev_path" ]] || continue
dev_class=$(cat "$dev_path/class" 2>/dev/null)
# Bridges belong to the isolation boundary, but vfio-pci does not
# support PCI bridges. Proxmox passes the endpoint devices only.
[[ "$dev_class" == 0x0604* || "$dev_class" == 0x0600* ]] && continue
basename "$dev_path"
done
}
_read_vfio_ids() {
local vfio_conf="/etc/modprobe.d/vfio.conf"
local ids_line ids_part
ids_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
[[ -z "$ids_line" ]] && return
ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//')
ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//' \
| tr '[:upper:]' '[:lower:]')
[[ -z "$ids_part" ]] && return
tr ',' '\n' <<< "$ids_part" | sed '/^$/d'
}
@@ -213,15 +234,10 @@ _remove_gpu_blacklist() {
local changed=false
case "$gpu_type" in
nvidia)
grep -qE '^blacklist (nouveau|nvidia|nvidiafb|nvidia_drm|nvidia_modeset|nvidia_uvm|lbm-nouveau)$|^options nouveau modeset=0$' "$blacklist_file" 2>/dev/null && changed=true
sed -i '/^blacklist nouveau$/d' "$blacklist_file"
sed -i '/^blacklist nvidia$/d' "$blacklist_file"
sed -i '/^blacklist nvidiafb$/d' "$blacklist_file"
sed -i '/^blacklist nvidia_drm$/d' "$blacklist_file"
sed -i '/^blacklist nvidia_modeset$/d' "$blacklist_file"
sed -i '/^blacklist nvidia_uvm$/d' "$blacklist_file"
sed -i '/^blacklist lbm-nouveau$/d' "$blacklist_file"
sed -i '/^options nouveau modeset=0$/d' "$blacklist_file"
# NVIDIA ownership is per BDF. Never alter the global blacklist here:
# it may belong to the host-driver installer and another NVIDIA GPU may
# still need the native driver.
return 1
;;
amd)
grep -qE '^blacklist (radeon|amdgpu)$' "$blacklist_file" 2>/dev/null && changed=true
@@ -243,14 +259,8 @@ _add_gpu_blacklist() {
touch "$blacklist_file"
case "$gpu_type" in
nvidia)
_add_line_if_missing "blacklist nouveau" "$blacklist_file"
_add_line_if_missing "blacklist nvidia" "$blacklist_file"
_add_line_if_missing "blacklist nvidiafb" "$blacklist_file"
_add_line_if_missing "blacklist nvidia_drm" "$blacklist_file"
_add_line_if_missing "blacklist nvidia_modeset" "$blacklist_file"
_add_line_if_missing "blacklist nvidia_uvm" "$blacklist_file"
_add_line_if_missing "blacklist lbm-nouveau" "$blacklist_file"
_add_line_if_missing "options nouveau modeset=0" "$blacklist_file"
# NVIDIA is handled exclusively by the shared per-BDF policy.
return 0
;;
amd)
_add_line_if_missing "blacklist radeon" "$blacklist_file"
@@ -263,174 +273,18 @@ _add_gpu_blacklist() {
}
_sanitize_nvidia_host_stack_for_vfio() {
local changed=false
local state_dir="/var/lib/proxmenux"
local state_file="${state_dir}/nvidia-host-services.state"
local svc
local -a services=(
"nvidia-persistenced.service"
"nvidia-powerd.service"
"nvidia-fabricmanager.service"
)
mkdir -p "$state_dir" >/dev/null 2>&1 || true
: > "$state_file"
for svc in "${services[@]}"; do
local was_enabled=0 was_active=0
if systemctl is-enabled --quiet "$svc" 2>/dev/null; then
was_enabled=1
fi
if systemctl is-active --quiet "$svc" 2>/dev/null; then
was_active=1
fi
if (( was_enabled == 1 || was_active == 1 )); then
echo "${svc} enabled=${was_enabled} active=${was_active}" >>"$state_file"
fi
if systemctl is-active --quiet "$svc" 2>/dev/null; then
systemctl stop "$svc" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
if systemctl is-enabled --quiet "$svc" 2>/dev/null; then
systemctl disable "$svc" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
done
[[ -s "$state_file" ]] || rm -f "$state_file"
if [[ -f /etc/modules-load.d/nvidia-vfio.conf ]]; then
mv /etc/modules-load.d/nvidia-vfio.conf /etc/modules-load.d/nvidia-vfio.conf.proxmenux-disabled-vfio >>"$LOG_FILE" 2>&1 || true
changed=true
fi
if grep -qE '^(nvidia|nvidia_uvm|nvidia_drm|nvidia_modeset)$' /etc/modules 2>/dev/null; then
sed -i '/^nvidia$/d;/^nvidia_uvm$/d;/^nvidia_drm$/d;/^nvidia_modeset$/d' /etc/modules
changed=true
fi
# Disable NVIDIA udev rules that trigger nvidia-smi (causes conflict with vfio-pci)
local udev_rules="/etc/udev/rules.d/70-nvidia.rules"
if [[ -f "$udev_rules" ]]; then
mv "$udev_rules" "${udev_rules}.proxmenux-disabled" >>"$LOG_FILE" 2>&1 || true
udevadm control --reload-rules >>"$LOG_FILE" 2>&1 || true
changed=true
fi
# Create hard blacklist to prevent ANY nvidia module loading (even via modprobe/nvidia-smi)
local nvidia_blacklist="/etc/modprobe.d/nvidia-blacklist.conf"
if [[ ! -f "$nvidia_blacklist" ]]; then
cat > "$nvidia_blacklist" <<'EOF'
# ProxMenux: Hard blacklist to prevent ANY nvidia module loading in VFIO mode
# This prevents nvidia-smi and other tools from triggering module load attempts
install nvidia /bin/false
install nvidia_uvm /bin/false
install nvidia_drm /bin/false
install nvidia_modeset /bin/false
EOF
changed=true
fi
if $changed; then
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'NVIDIA host services/autoload disabled for VFIO mode')" | tee -a "$screen_capture"
else
msg_ok "$(translate 'NVIDIA host services/autoload already aligned for VFIO mode')" | tee -a "$screen_capture"
fi
# Sync components_status.json — the host driver stays on disk but is
# not in use because the GPU now belongs to a VM. Prevents the update
# notification path (and any future logic gated on nvidia_driver.status)
# from acting on a state that no longer matches reality.
if declare -F update_component_status >/dev/null 2>&1; then
local _nvd_ver
_nvd_ver=$(jq -r '.nvidia_driver.version // ""' \
/usr/local/share/proxmenux/components_status.json 2>/dev/null)
update_component_status "nvidia_driver" "vfio_passthrough" \
"${_nvd_ver:-}" "gpu" '{"patched":false}' >>"$LOG_FILE" 2>&1 || true
fi
_proxmenux_nvidia_vfio_policy_sync || true
}
_restore_nvidia_host_stack_for_lxc() {
local changed=false
local state_file="/var/lib/proxmenux/nvidia-host-services.state"
local disabled_file="/etc/modules-load.d/nvidia-vfio.conf.proxmenux-disabled-vfio"
local active_file="/etc/modules-load.d/nvidia-vfio.conf"
# New per-BDF model: drop every NVIDIA BDF from the initramfs binder so
# the nvidia module reclaims the GPU after the next reboot. Idempotent:
# no-op if no NVIDIA BDFs are tracked. Vendor 10de = NVIDIA.
if declare -F _proxmenux_vfio_bind_purge_vendor >/dev/null 2>&1; then
_proxmenux_vfio_bind_purge_vendor "10de" && changed=true
fi
# Remove hard blacklist that was preventing nvidia module loading
local nvidia_blacklist="/etc/modprobe.d/nvidia-blacklist.conf"
if [[ -f "$nvidia_blacklist" ]]; then
rm -f "$nvidia_blacklist" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
# Restore NVIDIA udev rules if they were disabled
local udev_disabled="/etc/udev/rules.d/70-nvidia.rules.proxmenux-disabled"
local udev_rules="/etc/udev/rules.d/70-nvidia.rules"
if [[ -f "$udev_disabled" ]]; then
mv "$udev_disabled" "$udev_rules" >>"$LOG_FILE" 2>&1 || true
udevadm control --reload-rules >>"$LOG_FILE" 2>&1 || true
changed=true
fi
# Restore previous modules-load policy if ProxMenux disabled it in VM mode.
if [[ -f "$disabled_file" ]]; then
mv "$disabled_file" "$active_file" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
# Best effort: load NVIDIA kernel modules now that we are back in native mode.
# If not installed, these calls simply fail silently.
modprobe nvidia >/dev/null 2>&1 || true
modprobe nvidia_uvm >/dev/null 2>&1 || true
modprobe nvidia_modeset >/dev/null 2>&1 || true
modprobe nvidia_drm >/dev/null 2>&1 || true
if [[ -f "$state_file" ]]; then
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local svc enabled active
svc=$(echo "$line" | awk '{print $1}')
enabled=$(echo "$line" | awk -F'enabled=' '{print $2}' | awk '{print $1}')
active=$(echo "$line" | awk -F'active=' '{print $2}' | awk '{print $1}')
[[ "$enabled" == "1" ]] && systemctl enable "$svc" >>"$LOG_FILE" 2>&1 || true
[[ "$active" == "1" ]] && systemctl start "$svc" >>"$LOG_FILE" 2>&1 || true
done <"$state_file"
rm -f "$state_file"
changed=true
fi
if $changed; then
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'NVIDIA host services/autoload restored for native mode')" | tee -a "$screen_capture"
else
msg_ok "$(translate 'NVIDIA host services/autoload already aligned for native mode')" | tee -a "$screen_capture"
fi
# Sync components_status.json back to installed — the host has reclaimed
# the GPU and the nvidia stack is being reloaded. Restores the state to
# what it was before the VFIO switch so the update notification path and
# the auto-reinstall gate see the driver as active on the host again.
if declare -F update_component_status >/dev/null 2>&1; then
local _nvd_ver
_nvd_ver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1)
if [[ -z "$_nvd_ver" ]]; then
_nvd_ver=$(jq -r '.nvidia_driver.version // ""' \
/usr/local/share/proxmenux/components_status.json 2>/dev/null)
fi
update_component_status "nvidia_driver" "installed" \
"${_nvd_ver:-}" "gpu" '{"patched":false}' >>"$LOG_FILE" 2>&1 || true
_proxmenux_nvidia_vfio_policy_sync || true
if ! _proxmenux_all_nvidia_in_vfio; then
modprobe nvidia >/dev/null 2>&1 || true
modprobe nvidia_uvm >/dev/null 2>&1 || true
modprobe nvidia_modeset >/dev/null 2>&1 || true
modprobe nvidia_drm >/dev/null 2>&1 || true
fi
}
_add_amd_softdep() {
local vfio_conf="/etc/modprobe.d/vfio.conf"
_add_line_if_missing "softdep radeon pre: vfio-pci" "$vfio_conf"
@@ -468,6 +322,10 @@ _remove_vfio_modules_if_unused() {
local vfio_count
vfio_count=$(_read_vfio_ids | wc -l | tr -d '[:space:]')
[[ "$vfio_count" != "0" ]] && return 1
if declare -F _proxmenux_vfio_bind_has_entries >/dev/null 2>&1 \
&& _proxmenux_vfio_bind_has_entries; then
return 1
fi
local modules_file="/etc/modules"
[[ ! -f "$modules_file" ]] && return 1
local had_any=false
@@ -811,25 +669,37 @@ check_sriov_and_block_if_needed() {
collect_selected_iommu_ids() {
SELECTED_IOMMU_IDS=()
SELECTED_PCI_SLOTS=()
SELECTED_NVIDIA_BDFS=()
SELECTED_LEGACY_IOMMU_IDS=()
local idx pci viddid slot
local idx pci viddid slot selected_type bdf vid did gid
for idx in "${SELECTED_GPU_IDX[@]}"; do
pci="${ALL_GPU_PCIS[$idx]}"
viddid="${ALL_GPU_VIDDID[$idx]}"
selected_type="${ALL_GPU_TYPES[$idx]}"
slot="${pci#0000:}"
slot="${slot%.*}"
SELECTED_PCI_SLOTS+=("$slot")
local -a group_ids=()
mapfile -t group_ids < <(_get_iommu_group_ids "$pci")
if [[ ${#group_ids[@]} -gt 0 ]]; then
local gid
for gid in "${group_ids[@]}"; do
local -a group_bdfs=()
mapfile -t group_bdfs < <(_get_iommu_group_bdfs "$pci")
[[ ${#group_bdfs[@]} -gt 0 ]] || group_bdfs=("$pci")
for bdf in "${group_bdfs[@]}"; do
[[ "$bdf" == 0000:* ]] || bdf="0000:${bdf}"
vid=$(cat "/sys/bus/pci/devices/${bdf}/vendor" 2>/dev/null | sed 's/^0x//')
did=$(cat "/sys/bus/pci/devices/${bdf}/device" 2>/dev/null | sed 's/^0x//')
gid="${vid}:${did}"
if [[ -n "$vid" && -n "$did" ]]; then
_contains_in_array "$gid" "${SELECTED_IOMMU_IDS[@]}" || SELECTED_IOMMU_IDS+=("$gid")
done
elif [[ -n "$viddid" ]]; then
_contains_in_array "$viddid" "${SELECTED_IOMMU_IDS[@]}" || SELECTED_IOMMU_IDS+=("$viddid")
fi
if [[ "$selected_type" != "nvidia" ]]; then
_contains_in_array "$gid" "${SELECTED_LEGACY_IOMMU_IDS[@]}" || SELECTED_LEGACY_IOMMU_IDS+=("$gid")
fi
fi
if [[ "$selected_type" == "nvidia" ]]; then
_contains_in_array "$bdf" "${SELECTED_NVIDIA_BDFS[@]}" || SELECTED_NVIDIA_BDFS+=("$bdf")
fi
done
done
}
@@ -1167,6 +1037,9 @@ apply_vm_action_for_lxc_mode() {
if ! _contains_in_array "$_vd_id" "${SELECTED_IOMMU_IDS[@]}"; then
SELECTED_IOMMU_IDS+=("$_vd_id")
fi
if ! _contains_in_array "$_vd_id" "${SELECTED_LEGACY_IOMMU_IDS[@]}"; then
SELECTED_LEGACY_IOMMU_IDS+=("$_vd_id")
fi
fi
fi
fi
@@ -1234,6 +1107,12 @@ switch_to_vm_mode() {
msg_info "$(translate 'Configuring host for GPU -> VM mode...')"
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
if _contains_in_array "nvidia" "${selected_types[@]}"; then
_proxmenux_nvidia_migrate_legacy_blacklist
fi
if declare -F _pci_is_iommu_active >/dev/null 2>&1 && _pci_is_iommu_active; then
_register_iommu_tool
msg_ok "$(translate 'IOMMU is already active on this system')" | tee -a "$screen_capture"
@@ -1269,24 +1148,30 @@ switch_to_vm_mode() {
local -a current_ids=()
mapfile -t current_ids < <(_read_vfio_ids)
local id
for id in "${SELECTED_IOMMU_IDS[@]}"; do
for id in "${SELECTED_LEGACY_IOMMU_IDS[@]}"; do
_contains_in_array "$id" "${current_ids[@]}" || current_ids+=("$id")
done
_write_vfio_ids "${current_ids[@]}"
if [[ ${#SELECTED_IOMMU_IDS[@]} -gt 0 ]]; then
if [[ ${#SELECTED_LEGACY_IOMMU_IDS[@]} -gt 0 ]]; then
local ids_label
ids_label=$(IFS=','; echo "${SELECTED_IOMMU_IDS[*]}")
ids_label=$(IFS=','; echo "${SELECTED_LEGACY_IOMMU_IDS[*]}")
msg_ok "$(translate 'vfio-pci IDs configured') (${ids_label})" | tee -a "$screen_capture"
fi
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
local t
if [[ ${#SELECTED_NVIDIA_BDFS[@]} -gt 0 ]]; then
_proxmenux_vfio_bind_add_bdfs "${SELECTED_NVIDIA_BDFS[@]}"
msg_ok "$(translate 'NVIDIA per-BDF VFIO binding configured') (${SELECTED_NVIDIA_BDFS[*]})" | tee -a "$screen_capture"
fi
local t legacy_blacklist_configured=false
for t in "${selected_types[@]}"; do
[[ "$t" == "nvidia" ]] && continue
_add_gpu_blacklist "$t"
legacy_blacklist_configured=true
done
msg_ok "$(translate 'GPU host driver blacklisted in /etc/modprobe.d/blacklist.conf')" | tee -a "$screen_capture"
_contains_in_array "nvidia" "${selected_types[@]}" && _sanitize_nvidia_host_stack_for_vfio
$legacy_blacklist_configured \
&& msg_ok "$(translate 'GPU host driver blacklisted in /etc/modprobe.d/blacklist.conf')" | tee -a "$screen_capture"
_contains_in_array "nvidia" "${selected_types[@]}" && _proxmenux_nvidia_vfio_policy_sync || true
_contains_in_array "amd" "${selected_types[@]}" && _add_amd_softdep
if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then
@@ -1320,12 +1205,20 @@ switch_to_lxc_mode() {
msg_info "$(translate 'Removing VFIO ownership for selected GPU(s)...')"
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
if _contains_in_array "nvidia" "${selected_types[@]}"; then
_proxmenux_nvidia_migrate_legacy_blacklist
[[ ${#SELECTED_NVIDIA_BDFS[@]} -gt 0 ]] \
&& _proxmenux_vfio_bind_remove_bdfs "${SELECTED_NVIDIA_BDFS[@]}"
fi
local -a current_ids=() remaining_ids=() removed_ids=()
mapfile -t current_ids < <(_read_vfio_ids)
local id remove
for id in "${current_ids[@]}"; do
remove=false
_contains_in_array "$id" "${SELECTED_IOMMU_IDS[@]}" && remove=true
_contains_in_array "$id" "${SELECTED_LEGACY_IOMMU_IDS[@]}" && remove=true
if $remove; then
removed_ids+=("$id")
else
@@ -1339,17 +1232,16 @@ switch_to_lxc_mode() {
msg_ok "$(translate 'VFIO device IDs removed from /etc/modprobe.d/vfio.conf') (${ids_label})" | tee -a "$screen_capture"
fi
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
local t
for t in "${selected_types[@]}"; do
if [[ "$t" == "nvidia" ]]; then
_proxmenux_nvidia_vfio_policy_sync || true
continue
fi
if ! _type_has_remaining_vfio_ids "$t" "${remaining_ids[@]}"; then
if _remove_gpu_blacklist "$t"; then
msg_ok "$(translate 'Driver blacklist removed for') ${t}" | tee -a "$screen_capture"
fi
if [[ "$t" == "nvidia" ]]; then
_restore_nvidia_host_stack_for_lxc
fi
fi
done
+99 -203
View File
@@ -5,8 +5,8 @@
# Author : MacRimi
# Copyright : (c) 2024 MacRimi
# License : GPL-3.0
# Version : 1.0
# Last Updated: 09/04/2026
# Version : 1.1
# Last Updated: 26/08/2026
# ==========================================================
# This script is a hybrid version for ProxMenux Monitor.
# It accepts parameters to skip GPU selection and uses
@@ -36,6 +36,9 @@ if [[ -f "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS_LOCAL/global/pci_passthrough_helpers.sh"
elif [[ -f "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh" ]]; then
source "$LOCAL_SCRIPTS_DEFAULT/global/pci_passthrough_helpers.sh"
else
echo "ProxMenux: pci_passthrough_helpers.sh is required; refusing to change GPU ownership." >&2
exit 1
fi
load_language
initialize_cache
@@ -52,6 +55,8 @@ declare -a SELECTED_GPU_IDX=()
declare -a SELECTED_IOMMU_IDS=()
declare -a SELECTED_PCI_SLOTS=()
declare -a SELECTED_NVIDIA_BDFS=()
declare -a SELECTED_LEGACY_IOMMU_IDS=()
declare -a LXC_AFFECTED_CTIDS=()
declare -a LXC_AFFECTED_NAMES=()
@@ -145,12 +150,28 @@ _get_iommu_group_ids() {
done
}
_get_iommu_group_bdfs() {
local pci_full="$1"
local group_link="/sys/bus/pci/devices/${pci_full}/iommu_group"
[[ -L "$group_link" ]] || return 0
local group_dir dev_path dev_class
group_dir="/sys/kernel/iommu_groups/$(basename "$(readlink "$group_link")")/devices"
for dev_path in "${group_dir}/"*; do
[[ -e "$dev_path" ]] || continue
dev_class=$(cat "$dev_path/class" 2>/dev/null)
[[ "$dev_class" == 0x0604* || "$dev_class" == 0x0600* ]] && continue
basename "$dev_path"
done
}
_read_vfio_ids() {
local vfio_conf="/etc/modprobe.d/vfio.conf"
local ids_line ids_part
ids_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
[[ -z "$ids_line" ]] && return
ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//')
ids_part=$(echo "$ids_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//' \
| tr '[:upper:]' '[:lower:]')
[[ -z "$ids_part" ]] && return
tr ',' '\n' <<< "$ids_part" | sed '/^$/d'
}
@@ -191,15 +212,10 @@ _remove_gpu_blacklist() {
local changed=false
case "$gpu_type" in
nvidia)
grep -qE '^blacklist (nouveau|nvidia|nvidiafb|nvidia_drm|nvidia_modeset|nvidia_uvm|lbm-nouveau)$|^options nouveau modeset=0$' "$blacklist_file" 2>/dev/null && changed=true
sed -i '/^blacklist nouveau$/d' "$blacklist_file"
sed -i '/^blacklist nvidia$/d' "$blacklist_file"
sed -i '/^blacklist nvidiafb$/d' "$blacklist_file"
sed -i '/^blacklist nvidia_drm$/d' "$blacklist_file"
sed -i '/^blacklist nvidia_modeset$/d' "$blacklist_file"
sed -i '/^blacklist nvidia_uvm$/d' "$blacklist_file"
sed -i '/^blacklist lbm-nouveau$/d' "$blacklist_file"
sed -i '/^options nouveau modeset=0$/d' "$blacklist_file"
# NVIDIA ownership is per BDF. Never alter the global blacklist here:
# it may belong to the host-driver installer and another NVIDIA GPU may
# still need the native driver.
return 1
;;
amd)
grep -qE '^blacklist (radeon|amdgpu)$' "$blacklist_file" 2>/dev/null && changed=true
@@ -221,14 +237,8 @@ _add_gpu_blacklist() {
touch "$blacklist_file"
case "$gpu_type" in
nvidia)
_add_line_if_missing "blacklist nouveau" "$blacklist_file"
_add_line_if_missing "blacklist nvidia" "$blacklist_file"
_add_line_if_missing "blacklist nvidiafb" "$blacklist_file"
_add_line_if_missing "blacklist nvidia_drm" "$blacklist_file"
_add_line_if_missing "blacklist nvidia_modeset" "$blacklist_file"
_add_line_if_missing "blacklist nvidia_uvm" "$blacklist_file"
_add_line_if_missing "blacklist lbm-nouveau" "$blacklist_file"
_add_line_if_missing "options nouveau modeset=0" "$blacklist_file"
# NVIDIA is handled exclusively by the shared per-BDF policy.
return 0
;;
amd)
_add_line_if_missing "blacklist radeon" "$blacklist_file"
@@ -241,170 +251,18 @@ _add_gpu_blacklist() {
}
_sanitize_nvidia_host_stack_for_vfio() {
local changed=false
local state_dir="/var/lib/proxmenux"
local state_file="${state_dir}/nvidia-host-services.state"
local svc
local -a services=(
"nvidia-persistenced.service"
"nvidia-powerd.service"
"nvidia-fabricmanager.service"
)
mkdir -p "$state_dir" >/dev/null 2>&1 || true
: > "$state_file"
for svc in "${services[@]}"; do
local was_enabled=0 was_active=0
if systemctl is-enabled --quiet "$svc" 2>/dev/null; then
was_enabled=1
fi
if systemctl is-active --quiet "$svc" 2>/dev/null; then
was_active=1
fi
if (( was_enabled == 1 || was_active == 1 )); then
echo "${svc} enabled=${was_enabled} active=${was_active}" >>"$state_file"
fi
if systemctl is-active --quiet "$svc" 2>/dev/null; then
systemctl stop "$svc" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
if systemctl is-enabled --quiet "$svc" 2>/dev/null; then
systemctl disable "$svc" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
done
[[ -s "$state_file" ]] || rm -f "$state_file"
if [[ -f /etc/modules-load.d/nvidia-vfio.conf ]]; then
mv /etc/modules-load.d/nvidia-vfio.conf /etc/modules-load.d/nvidia-vfio.conf.proxmenux-disabled-vfio >>"$LOG_FILE" 2>&1 || true
changed=true
fi
if grep -qE '^(nvidia|nvidia_uvm|nvidia_drm|nvidia_modeset)$' /etc/modules 2>/dev/null; then
sed -i '/^nvidia$/d;/^nvidia_uvm$/d;/^nvidia_drm$/d;/^nvidia_modeset$/d' /etc/modules
changed=true
fi
# Disable NVIDIA udev rules that trigger nvidia-smi (causes conflict with vfio-pci)
local udev_rules="/etc/udev/rules.d/70-nvidia.rules"
if [[ -f "$udev_rules" ]]; then
mv "$udev_rules" "${udev_rules}.proxmenux-disabled" >>"$LOG_FILE" 2>&1 || true
udevadm control --reload-rules >>"$LOG_FILE" 2>&1 || true
changed=true
fi
# Create hard blacklist to prevent ANY nvidia module loading (even via modprobe/nvidia-smi)
local nvidia_blacklist="/etc/modprobe.d/nvidia-blacklist.conf"
if [[ ! -f "$nvidia_blacklist" ]]; then
cat > "$nvidia_blacklist" <<'EOF'
# ProxMenux: Hard blacklist to prevent ANY nvidia module loading in VFIO mode
# This prevents nvidia-smi and other tools from triggering module load attempts
install nvidia /bin/false
install nvidia_uvm /bin/false
install nvidia_drm /bin/false
install nvidia_modeset /bin/false
EOF
changed=true
fi
if $changed; then
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'NVIDIA host services/autoload disabled for VFIO mode')" | tee -a "$screen_capture"
else
msg_ok "$(translate 'NVIDIA host services/autoload already aligned for VFIO mode')" | tee -a "$screen_capture"
fi
# Sync components_status.json — the host driver stays on disk but is
# not in use because the GPU now belongs to a VM. Prevents the update
# notification path (and any future logic gated on nvidia_driver.status)
# from acting on a state that no longer matches reality.
if declare -F update_component_status >/dev/null 2>&1; then
local _nvd_ver
_nvd_ver=$(jq -r '.nvidia_driver.version // ""' \
/usr/local/share/proxmenux/components_status.json 2>/dev/null)
update_component_status "nvidia_driver" "vfio_passthrough" \
"${_nvd_ver:-}" "gpu" '{"patched":false}' >>"$LOG_FILE" 2>&1 || true
fi
_proxmenux_nvidia_vfio_policy_sync || true
}
_restore_nvidia_host_stack_for_lxc() {
local changed=false
local state_file="/var/lib/proxmenux/nvidia-host-services.state"
local disabled_file="/etc/modules-load.d/nvidia-vfio.conf.proxmenux-disabled-vfio"
local active_file="/etc/modules-load.d/nvidia-vfio.conf"
# New per-BDF model: drop every NVIDIA BDF from the initramfs binder so
# the nvidia module reclaims the GPU after the next reboot. Idempotent.
if declare -F _proxmenux_vfio_bind_purge_vendor >/dev/null 2>&1; then
_proxmenux_vfio_bind_purge_vendor "10de" && changed=true
fi
# Remove hard blacklist that was preventing nvidia module loading
local nvidia_blacklist="/etc/modprobe.d/nvidia-blacklist.conf"
if [[ -f "$nvidia_blacklist" ]]; then
rm -f "$nvidia_blacklist" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
# Restore NVIDIA udev rules if they were disabled
local udev_disabled="/etc/udev/rules.d/70-nvidia.rules.proxmenux-disabled"
local udev_rules="/etc/udev/rules.d/70-nvidia.rules"
if [[ -f "$udev_disabled" ]]; then
mv "$udev_disabled" "$udev_rules" >>"$LOG_FILE" 2>&1 || true
udevadm control --reload-rules >>"$LOG_FILE" 2>&1 || true
changed=true
fi
if [[ -f "$disabled_file" ]]; then
mv "$disabled_file" "$active_file" >>"$LOG_FILE" 2>&1 || true
changed=true
fi
modprobe nvidia >/dev/null 2>&1 || true
modprobe nvidia_uvm >/dev/null 2>&1 || true
modprobe nvidia_modeset >/dev/null 2>&1 || true
modprobe nvidia_drm >/dev/null 2>&1 || true
if [[ -f "$state_file" ]]; then
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local svc enabled active
svc=$(echo "$line" | awk '{print $1}')
enabled=$(echo "$line" | awk -F'enabled=' '{print $2}' | awk '{print $1}')
active=$(echo "$line" | awk -F'active=' '{print $2}' | awk '{print $1}')
[[ "$enabled" == "1" ]] && systemctl enable "$svc" >>"$LOG_FILE" 2>&1 || true
[[ "$active" == "1" ]] && systemctl start "$svc" >>"$LOG_FILE" 2>&1 || true
done <"$state_file"
rm -f "$state_file"
changed=true
fi
if $changed; then
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'NVIDIA host services/autoload restored for native mode')" | tee -a "$screen_capture"
else
msg_ok "$(translate 'NVIDIA host services/autoload already aligned for native mode')" | tee -a "$screen_capture"
fi
# Sync components_status.json back to installed — the host has reclaimed
# the GPU and the nvidia stack is being reloaded. Restores the state to
# what it was before the VFIO switch so the update notification path and
# the auto-reinstall gate see the driver as active on the host again.
if declare -F update_component_status >/dev/null 2>&1; then
local _nvd_ver
_nvd_ver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1)
if [[ -z "$_nvd_ver" ]]; then
_nvd_ver=$(jq -r '.nvidia_driver.version // ""' \
/usr/local/share/proxmenux/components_status.json 2>/dev/null)
fi
update_component_status "nvidia_driver" "installed" \
"${_nvd_ver:-}" "gpu" '{"patched":false}' >>"$LOG_FILE" 2>&1 || true
_proxmenux_nvidia_vfio_policy_sync || true
if ! _proxmenux_all_nvidia_in_vfio; then
modprobe nvidia >/dev/null 2>&1 || true
modprobe nvidia_uvm >/dev/null 2>&1 || true
modprobe nvidia_modeset >/dev/null 2>&1 || true
modprobe nvidia_drm >/dev/null 2>&1 || true
fi
}
_add_amd_softdep() {
local vfio_conf="/etc/modprobe.d/vfio.conf"
_add_line_if_missing "softdep radeon pre: vfio-pci" "$vfio_conf"
@@ -442,6 +300,10 @@ _remove_vfio_modules_if_unused() {
local vfio_count
vfio_count=$(_read_vfio_ids | wc -l | tr -d '[:space:]')
[[ "$vfio_count" != "0" ]] && return 1
if declare -F _proxmenux_vfio_bind_has_entries >/dev/null 2>&1 \
&& _proxmenux_vfio_bind_has_entries; then
return 1
fi
local modules_file="/etc/modules"
[[ ! -f "$modules_file" ]] && return 1
local had_any=false
@@ -632,25 +494,37 @@ validate_vm_mode_blocked_ids() {
collect_selected_iommu_ids() {
SELECTED_IOMMU_IDS=()
SELECTED_PCI_SLOTS=()
SELECTED_NVIDIA_BDFS=()
SELECTED_LEGACY_IOMMU_IDS=()
local idx pci viddid slot
local idx pci viddid slot selected_type bdf vid did gid
for idx in "${SELECTED_GPU_IDX[@]}"; do
pci="${ALL_GPU_PCIS[$idx]}"
viddid="${ALL_GPU_VIDDID[$idx]}"
selected_type="${ALL_GPU_TYPES[$idx]}"
slot="${pci#0000:}"
slot="${slot%.*}"
SELECTED_PCI_SLOTS+=("$slot")
local -a group_ids=()
mapfile -t group_ids < <(_get_iommu_group_ids "$pci")
if [[ ${#group_ids[@]} -gt 0 ]]; then
local gid
for gid in "${group_ids[@]}"; do
local -a group_bdfs=()
mapfile -t group_bdfs < <(_get_iommu_group_bdfs "$pci")
[[ ${#group_bdfs[@]} -gt 0 ]] || group_bdfs=("$pci")
for bdf in "${group_bdfs[@]}"; do
[[ "$bdf" == 0000:* ]] || bdf="0000:${bdf}"
vid=$(cat "/sys/bus/pci/devices/${bdf}/vendor" 2>/dev/null | sed 's/^0x//')
did=$(cat "/sys/bus/pci/devices/${bdf}/device" 2>/dev/null | sed 's/^0x//')
gid="${vid}:${did}"
if [[ -n "$vid" && -n "$did" ]]; then
_contains_in_array "$gid" "${SELECTED_IOMMU_IDS[@]}" || SELECTED_IOMMU_IDS+=("$gid")
done
elif [[ -n "$viddid" ]]; then
_contains_in_array "$viddid" "${SELECTED_IOMMU_IDS[@]}" || SELECTED_IOMMU_IDS+=("$viddid")
fi
if [[ "$selected_type" != "nvidia" ]]; then
_contains_in_array "$gid" "${SELECTED_LEGACY_IOMMU_IDS[@]}" || SELECTED_LEGACY_IOMMU_IDS+=("$gid")
fi
fi
if [[ "$selected_type" == "nvidia" ]]; then
_contains_in_array "$bdf" "${SELECTED_NVIDIA_BDFS[@]}" || SELECTED_NVIDIA_BDFS+=("$bdf")
fi
done
done
}
@@ -948,6 +822,9 @@ apply_vm_action_for_lxc_mode() {
if ! _contains_in_array "$_vd_id" "${SELECTED_IOMMU_IDS[@]}"; then
SELECTED_IOMMU_IDS+=("$_vd_id")
fi
if ! _contains_in_array "$_vd_id" "${SELECTED_LEGACY_IOMMU_IDS[@]}"; then
SELECTED_LEGACY_IOMMU_IDS+=("$_vd_id")
fi
fi
fi
fi
@@ -1018,6 +895,12 @@ switch_to_vm_mode() {
msg_info "$(translate 'Configuring host for GPU -> VM mode...')"
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
if _contains_in_array "nvidia" "${selected_types[@]}"; then
_proxmenux_nvidia_migrate_legacy_blacklist
fi
if declare -F _pci_is_iommu_active >/dev/null 2>&1 && _pci_is_iommu_active; then
_register_iommu_tool
msg_ok "$(translate 'IOMMU is already active on this system')" | tee -a "$screen_capture"
@@ -1044,24 +927,30 @@ switch_to_vm_mode() {
local -a current_ids=()
mapfile -t current_ids < <(_read_vfio_ids)
local id
for id in "${SELECTED_IOMMU_IDS[@]}"; do
for id in "${SELECTED_LEGACY_IOMMU_IDS[@]}"; do
_contains_in_array "$id" "${current_ids[@]}" || current_ids+=("$id")
done
_write_vfio_ids "${current_ids[@]}"
if [[ ${#SELECTED_IOMMU_IDS[@]} -gt 0 ]]; then
if [[ ${#SELECTED_LEGACY_IOMMU_IDS[@]} -gt 0 ]]; then
local ids_label
ids_label=$(IFS=','; echo "${SELECTED_IOMMU_IDS[*]}")
ids_label=$(IFS=','; echo "${SELECTED_LEGACY_IOMMU_IDS[*]}")
msg_ok "$(translate 'vfio-pci IDs configured') (${ids_label})" | tee -a "$screen_capture"
fi
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
local t
if [[ ${#SELECTED_NVIDIA_BDFS[@]} -gt 0 ]]; then
_proxmenux_vfio_bind_add_bdfs "${SELECTED_NVIDIA_BDFS[@]}"
msg_ok "$(translate 'NVIDIA per-BDF VFIO binding configured') (${SELECTED_NVIDIA_BDFS[*]})" | tee -a "$screen_capture"
fi
local t legacy_blacklist_configured=false
for t in "${selected_types[@]}"; do
[[ "$t" == "nvidia" ]] && continue
_add_gpu_blacklist "$t"
legacy_blacklist_configured=true
done
msg_ok "$(translate 'GPU host driver blacklisted in /etc/modprobe.d/blacklist.conf')" | tee -a "$screen_capture"
_contains_in_array "nvidia" "${selected_types[@]}" && _sanitize_nvidia_host_stack_for_vfio
$legacy_blacklist_configured \
&& msg_ok "$(translate 'GPU host driver blacklisted in /etc/modprobe.d/blacklist.conf')" | tee -a "$screen_capture"
_contains_in_array "nvidia" "${selected_types[@]}" && _proxmenux_nvidia_vfio_policy_sync || true
_contains_in_array "amd" "${selected_types[@]}" && _add_amd_softdep
if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then
@@ -1095,12 +984,20 @@ switch_to_lxc_mode() {
msg_info "$(translate 'Removing VFIO ownership for selected GPU(s)...')"
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
if _contains_in_array "nvidia" "${selected_types[@]}"; then
_proxmenux_nvidia_migrate_legacy_blacklist
[[ ${#SELECTED_NVIDIA_BDFS[@]} -gt 0 ]] \
&& _proxmenux_vfio_bind_remove_bdfs "${SELECTED_NVIDIA_BDFS[@]}"
fi
local -a current_ids=() remaining_ids=() removed_ids=()
mapfile -t current_ids < <(_read_vfio_ids)
local id remove
for id in "${current_ids[@]}"; do
remove=false
_contains_in_array "$id" "${SELECTED_IOMMU_IDS[@]}" && remove=true
_contains_in_array "$id" "${SELECTED_LEGACY_IOMMU_IDS[@]}" && remove=true
if $remove; then
removed_ids+=("$id")
else
@@ -1114,17 +1011,16 @@ switch_to_lxc_mode() {
msg_ok "$(translate 'VFIO device IDs removed from /etc/modprobe.d/vfio.conf') (${ids_label})" | tee -a "$screen_capture"
fi
local -a selected_types=()
mapfile -t selected_types < <(_selected_types_unique)
local t
for t in "${selected_types[@]}"; do
if [[ "$t" == "nvidia" ]]; then
_proxmenux_nvidia_vfio_policy_sync || true
continue
fi
if ! _type_has_remaining_vfio_ids "$t" "${remaining_ids[@]}"; then
if _remove_gpu_blacklist "$t"; then
msg_ok "$(translate 'Driver blacklist removed for') ${t}" | tee -a "$screen_capture"
fi
if [[ "$t" == "nvidia" ]]; then
_restore_nvidia_host_stack_for_lxc
fi
fi
done