Record GPU/TPU host changes in the change journal

Seven gpu_tpu scripts now write through the journal for host changes only — driver and package installs, vfio binding, modprobe.d, /etc/modules, GRUB and udev — while VM and LXC configuration stays out; add_gpu_vm and install_coral_lxc journal only their host writes. amd_gpu_tools records its install, and pmx_journal.sh gains pmx_record_uninstall while changes_journal.py retires an installed package when ProxMenux removes it. What each function writes is byte-identical to before, verified by tests/journal/verify_journal_migration.py.
This commit is contained in:
MacRimi
2026-09-11 21:08:35 +02:00
parent 9c930de186
commit 2a672889bf
10 changed files with 373 additions and 117 deletions
+7
View File
@@ -255,6 +255,13 @@ def ingest(limit: int = 5000) -> int:
try:
conn.execute("BEGIN IMMEDIATE")
for r in rows:
if r[3] == "uninstall_package":
# A removal retires the package's install entry; the current
# state is that it is no longer on the host.
conn.execute(
"DELETE FROM changes WHERE class = ? AND target = ?",
(CLASS_INSTALLATION, r[7]))
continue
if r[2] == CLASS_INSTALLATION:
# A package is one entry however often, or by whichever helper,
# it is installed.
+12
View File
@@ -419,6 +419,18 @@ pmx_record_install() {
"capture=created" "revert=purge" "exactness=none"
}
# Records that ProxMenux removed a package. On ingest this retires the
# package's installation entry, so a package installed and later removed no
# longer shows as present — the journal reflects the current state.
pmx_record_uninstall() {
local packages="$1" version="${2:-1.0}"
local -a fields
mapfile -t fields < <(_pmx_journal_common)
_pmx_journal_record "${fields[@]}" \
"class=installation" "operation=uninstall_package" "target=$packages" \
"function_version=$version" "capture=none" "revert=none" "exactness=none"
}
pmx_record_applied() {
local tool="$1" version="$2" state="${3:-applied}"
local -a fields
+58 -21
View File
@@ -42,6 +42,9 @@ screen_capture="/tmp/proxmenux_add_gpu_vm_screen_$$.txt"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
fi
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
@@ -113,11 +116,12 @@ _get_pci_driver() {
}
_add_line_if_missing() {
pmx_journal_context "_add_line_if_missing" "1.1" "add_gpu_vm.sh"
local line="$1"
local file="$2"
touch "$file"
[[ -f "$file" ]] || pmx_write_file "$file" < /dev/null
if ! grep -qF "$line" "$file"; then
echo "$line" >> "$file"
echo "$line" | pmx_append_file "$file"
HOST_CONFIG_CHANGED=true
fi
}
@@ -670,6 +674,7 @@ check_iommu_enabled() {
}
_enable_iommu_cmdline() {
pmx_journal_context "_enable_iommu_cmdline" "1.1" "add_gpu_vm.sh"
local cpu_vendor
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
@@ -692,8 +697,9 @@ _enable_iommu_cmdline() {
# systemd-boot / ZFS
if ! grep -q "$iommu_param" "$cmdline_file"; then
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
proxmox-boot-tool refresh >/dev/null 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
msg_ok "$(translate 'IOMMU parameters added to /etc/kernel/cmdline')"
else
msg_ok "$(translate 'IOMMU already configured in /etc/kernel/cmdline')"
@@ -702,8 +708,9 @@ _enable_iommu_cmdline() {
# GRUB
if ! grep -q "$iommu_param" "$grub_file"; then
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
update-grub >/dev/null 2>&1 || true
pmx_record_execution "update GRUB configuration" "update-grub"
msg_ok "$(translate 'IOMMU parameters added to GRUB')"
else
msg_ok "$(translate 'IOMMU already configured in GRUB')"
@@ -1613,9 +1620,12 @@ add_vfio_modules() {
# ── vfio-pci IDs — merge with existing ones ─────────────
configure_vfio_pci_ids() {
pmx_journal_context "configure_vfio_pci_ids" "1.1" "add_gpu_vm.sh"
msg_info "$(translate 'Configuring vfio-pci binding...')"
local vfio_conf="/etc/modprobe.d/vfio.conf"
touch "$vfio_conf"
local vfio_tmp
vfio_tmp=$(mktemp)
[[ -f "$vfio_conf" ]] && cat "$vfio_conf" > "$vfio_tmp"
# ────────────────────────────────────────────────────────────────
# NVIDIA: per-BDF binding (multi-GPU safe). The `options vfio-pci
@@ -1629,9 +1639,9 @@ configure_vfio_pci_ids() {
# Clean up any previous ids= line that captured this NVIDIA
# (older versions of this script wrote it; remove to avoid
# collateral grabs on sibling GPUs of the same model).
if grep -qE '^options vfio-pci ids=' "$vfio_conf" 2>/dev/null; then
if grep -qE '^options vfio-pci ids=' "$vfio_tmp" 2>/dev/null; then
local existing_line ids_part
existing_line=$(grep '^options vfio-pci ids=' "$vfio_conf" | head -1)
existing_line=$(grep '^options vfio-pci ids=' "$vfio_tmp" | head -1)
ids_part=$(echo "$existing_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//')
local kept=()
@@ -1644,20 +1654,30 @@ configure_vfio_pci_ids() {
$drop || kept+=("$eid")
done
sed -i '/^options vfio-pci ids=/d' "$vfio_conf"
sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next"
mv "${vfio_tmp}.next" "$vfio_tmp"
if [[ ${#kept[@]} -gt 0 ]]; then
local kept_str
kept_str=$(IFS=','; echo "${kept[*]}")
echo "options vfio-pci ids=${kept_str} disable_vga=1" >> "$vfio_conf"
echo "options vfio-pci ids=${kept_str} disable_vga=1" >> "$vfio_tmp"
fi
HOST_CONFIG_CHANGED=true
fi
# Ensure vfio loads before nvidia so the per-BDF override wins.
_add_line_if_missing "softdep nvidia pre: vfio-pci" "$vfio_conf"
_add_line_if_missing "softdep nvidia_drm pre: vfio-pci" "$vfio_conf"
_add_line_if_missing "softdep nvidia_modeset pre: vfio-pci" "$vfio_conf"
_add_line_if_missing "softdep nvidia_uvm pre: vfio-pci" "$vfio_conf"
local softdep
for softdep in \
"softdep nvidia pre: vfio-pci" \
"softdep nvidia_drm pre: vfio-pci" \
"softdep nvidia_modeset pre: vfio-pci" \
"softdep nvidia_uvm pre: vfio-pci"; do
if ! grep -qF "$softdep" "$vfio_tmp"; then
echo "$softdep" >> "$vfio_tmp"
HOST_CONFIG_CHANGED=true
fi
done
pmx_write_file "$vfio_conf" < "$vfio_tmp"
rm -f "$vfio_tmp"
# Per-BDF binder rule. IOMMU_DEVICES has the BDFs for the GPU
# we're passing (and any same-group functions like the audio
@@ -1682,7 +1702,7 @@ configure_vfio_pci_ids() {
# Collect existing IDs (if any)
local existing_ids=()
local existing_line
existing_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
existing_line=$(grep "^options vfio-pci ids=" "$vfio_tmp" 2>/dev/null | head -1)
if [[ -n "$existing_line" ]]; then
local ids_part
ids_part=$(echo "$existing_line" | grep -oE 'ids=[^[:space:]]+' | sed 's/ids=//')
@@ -1703,13 +1723,16 @@ configure_vfio_pci_ids() {
ids_str=$(IFS=','; echo "${all_ids[*]}")
local existing_full_line
existing_full_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
existing_full_line=$(grep "^options vfio-pci ids=" "$vfio_tmp" 2>/dev/null | head -1)
local new_full_line="options vfio-pci ids=${ids_str} disable_vga=1"
if [[ "$existing_full_line" != "$new_full_line" ]]; then
sed -i '/^options vfio-pci ids=/d' "$vfio_conf"
echo "$new_full_line" >> "$vfio_conf"
sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next"
mv "${vfio_tmp}.next" "$vfio_tmp"
echo "$new_full_line" >> "$vfio_tmp"
HOST_CONFIG_CHANGED=true
fi
pmx_write_file "$vfio_conf" < "$vfio_tmp"
rm -f "$vfio_tmp"
msg_ok "$(translate 'vfio-pci IDs configured') (${ids_str})" | tee -a "$screen_capture"
}
@@ -1787,6 +1810,7 @@ sanitize_nvidia_host_stack_for_vfio() {
# ── AMD ROM dump: sysfs first, VFCT ACPI table as fallback ───────────────
_dump_rom_via_vfct() {
pmx_journal_context "_dump_rom_via_vfct" "1.1" "add_gpu_vm.sh"
local rom_dest="$1"
local vfct_file="/sys/firmware/acpi/tables/VFCT"
[[ -f "$vfct_file" ]] || return 1
@@ -1808,11 +1832,16 @@ _dump_rom_via_vfct() {
return 1
fi
dd if="$vfct_file" bs=1 skip=64 count="$img_length" of="$rom_dest" 2>/dev/null
local rom_tmp
rom_tmp=$(mktemp)
dd if="$vfct_file" bs=1 skip=64 count="$img_length" of="$rom_tmp" 2>/dev/null
pmx_write_file "$rom_dest" < "$rom_tmp"
rm -f "$rom_tmp"
[[ -s "$rom_dest" ]]
}
dump_amd_rom() {
pmx_journal_context "dump_amd_rom" "1.1" "add_gpu_vm.sh"
local pci_full="$SELECTED_GPU_PCI"
local rom_path="/sys/bus/pci/devices/${pci_full}/rom"
local kvm_dir="/usr/share/kvm"
@@ -1828,14 +1857,19 @@ dump_amd_rom() {
if [[ -f "$rom_path" ]]; then
msg_info "$(translate 'Dumping AMD GPU ROM BIOS via sysfs...')"
echo 1 > "$rom_path" 2>/dev/null
if cat "$rom_path" > "$rom_dest" 2>>"$LOG_FILE" && [[ -s "$rom_dest" ]]; then
local rom_tmp
rom_tmp=$(mktemp)
if cat "$rom_path" > "$rom_tmp" 2>>"$LOG_FILE" && [[ -s "$rom_tmp" ]]; then
pmx_write_file "$rom_dest" < "$rom_tmp"
rm -f "$rom_tmp"
echo 0 > "$rom_path" 2>/dev/null
AMD_ROM_FILE="$rom_filename"
msg_ok "$(translate 'GPU ROM dumped to') ${rom_dest}" | tee -a "$screen_capture"
return 0
fi
rm -f "$rom_tmp"
echo 0 > "$rom_path" 2>/dev/null
rm -f "$rom_dest"
pmx_remove_file "$rom_dest"
msg_warn "$(translate 'sysfs ROM dump failed — trying ACPI VFCT table...')"
else
msg_info "$(translate 'No sysfs ROM entry — trying ACPI VFCT table...')"
@@ -1848,7 +1882,7 @@ dump_amd_rom() {
return 0
fi
rm -f "$rom_dest"
pmx_remove_file "$rom_dest"
msg_warn "$(translate 'ROM dump not available — configuring without romfile.')"
msg_warn "$(translate 'Passthrough may still work without a ROM file.')"
}
@@ -2159,11 +2193,14 @@ _configure_nvidia_kvm_hide() {
# ── Update initramfs ─────────────────────────────────────
update_initramfs_host() {
pmx_journal_context "update_initramfs_host" "1.1" "add_gpu_vm.sh"
msg_info "$(translate 'Updating initramfs (this may take a minute)...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1
pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all"
# Copy the freshly-built initramfs to the EFI System Partition.
# Without this the bootloader keeps using the previous initramfs.
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture"
}
+10
View File
@@ -24,6 +24,10 @@ if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
fi
if [[ ! -f "$COMPONENTS_STATUS_FILE" ]]; then
echo "{}" > "$COMPONENTS_STATUS_FILE"
fi
@@ -140,6 +144,9 @@ install_amdgpu_top() {
fi
msg_ok "$(translate 'amdgpu_top installed successfully')"
pmx_journal_context "install_amdgpu_top" "1.0" "amd_gpu_tools.sh"
pmx_record_install "amdgpu-top"
# Clean up
rm -rf "$tmp_dir"
@@ -160,6 +167,9 @@ uninstall_amdgpu_top() {
if dpkg -r amdgpu-top >>"$LOG_FILE" 2>&1 || apt-get remove -y amdgpu-top >>"$LOG_FILE" 2>&1; then
msg_ok "$(translate 'amdgpu_top uninstalled successfully')"
pmx_journal_context "uninstall_amdgpu_top" "1.0" "amd_gpu_tools.sh"
pmx_record_uninstall "amdgpu-top"
if type update_component_status &>/dev/null; then
update_component_status "amdgpu_top" "uninstalled" "" "gpu" '{}'
+77 -25
View File
@@ -52,6 +52,10 @@ if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
fi
load_language
initialize_cache
@@ -130,16 +134,18 @@ pre_install_prompt() {
# ============================================================
ensure_apex_group_and_udev() {
pmx_journal_context "ensure_apex_group_and_udev" "2.0" "install_coral.sh"
msg_info "$(translate 'Ensuring apex group and udev rules...')"
if ! getent group apex >/dev/null; then
groupadd --system apex || true
pmx_apply_setting "group:apex" "getent group apex 2>/dev/null || true" \
groupadd --system apex || true
msg_ok "$(translate 'System group apex created.')"
else
msg_ok "$(translate 'System group apex already exists.')"
fi
cat >/etc/udev/rules.d/99-coral-apex.rules <<'EOF'
pmx_write_file /etc/udev/rules.d/99-coral-apex.rules <<'EOF'
# Coral / Google APEX TPU (M.2 / PCIe)
# Assign group "apex" and safe permissions to device nodes
KERNEL=="apex_*", GROUP="apex", MODE="0660"
@@ -147,11 +153,14 @@ SUBSYSTEM=="apex", GROUP="apex", MODE="0660"
EOF
if [[ -f /usr/lib/udev/rules.d/60-gasket-dkms.rules ]]; then
sed -i 's/GROUP="[^"]*"/GROUP="apex"/g' /usr/lib/udev/rules.d/60-gasket-dkms.rules || true
pmx_edit_file /usr/lib/udev/rules.d/60-gasket-dkms.rules \
's/GROUP="[^"]*"/GROUP="apex"/g' || true
fi
udevadm control --reload-rules
pmx_record_execution "Reload Coral APEX udev rules" "udevadm control --reload-rules"
udevadm trigger --subsystem-match=apex || true
pmx_record_execution "Trigger Coral APEX udev rules" "udevadm trigger --subsystem-match=apex"
msg_ok "$(translate 'apex group and udev rules are in place.')"
@@ -163,6 +172,7 @@ EOF
}
cleanup_broken_gasket_dkms() {
pmx_journal_context "cleanup_broken_gasket_dkms" "2.0" "install_coral.sh"
# Recover from a broken gasket-dkms .deb state (half-configured, unpacked,
# half-installed). This is a common failure mode on PVE 9 kernel upgrades:
# dkms autoinstall tries to rebuild against the new kernel, fails, and
@@ -175,15 +185,21 @@ cleanup_broken_gasket_dkms() {
case "$pkg_state" in
ii|rc)
msg_info "$(translate 'Removing any pre-existing gasket-dkms package...')"
dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1 || true
if dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1; then
pmx_record_uninstall "gasket-dkms" "2.0"
fi
dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Remove gasket DKMS entry" "dkms remove gasket/1.0 --all"
msg_ok "$(translate 'Pre-existing gasket-dkms package removed.')"
;;
*)
msg_warn "$(translate 'Detected broken gasket-dkms package state:') ${pkg_state}. $(translate 'Forcing removal...')"
dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1 || true
if dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1; then
pmx_record_uninstall "gasket-dkms" "2.0"
fi
dpkg --purge --force-all gasket-dkms >>"$LOG_FILE" 2>&1 || true
dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Remove broken gasket DKMS entry" "dkms remove gasket/1.0 --all"
apt-get install -f -y >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'Broken gasket-dkms package state recovered.')"
;;
@@ -244,6 +260,7 @@ detect_orphan_gasket_dkms() {
}
cleanup_orphan_gasket_dkms() {
pmx_journal_context "cleanup_orphan_gasket_dkms" "2.0" "install_coral.sh"
# Return codes:
# 0 cleanup completed and every final verification passed
# 1 operator cancelled before any change was made
@@ -285,6 +302,9 @@ cleanup_orphan_gasket_dkms() {
dpkg --remove --force-remove-reinstreq gasket-dkms >>"$LOG_FILE" 2>&1 || true
dpkg --purge --force-all gasket-dkms >>"$LOG_FILE" 2>&1 || true
fi
if ! dpkg-query -W gasket-dkms >/dev/null 2>&1; then
pmx_record_uninstall "gasket-dkms" "2.0"
fi
# A host can retain more than the historical gasket/1.0 entry. Read
# every version known by DKMS and also include stale version trees
@@ -309,6 +329,7 @@ cleanup_orphan_gasket_dkms() {
if ! dkms remove -m gasket -v "$version" --all >>"$LOG_FILE" 2>&1; then
dkms_remove_failed=1
fi
pmx_record_execution "Remove orphan gasket DKMS entry" "dkms remove -m gasket -v $version --all"
done <<<"$versions"
if [[ "$dkms_remove_failed" -eq 0 ]]; then
@@ -383,7 +404,7 @@ cleanup_orphan_gasket_dkms() {
if declare -f update_component_status >/dev/null 2>&1; then
update_component_status "coral_driver" "removed" "" "gpu" '{}' >/dev/null 2>&1 || true
fi
rm -f /var/lib/proxmenux/coral_gasket_version 2>/dev/null || true
pmx_remove_file /var/lib/proxmenux/coral_gasket_version || true
fi
if [[ "$repair_failed" -ne 0 ]]; then
@@ -450,6 +471,7 @@ show_dkms_build_failure() {
}
install_gasket_apex_dkms() {
pmx_journal_context "install_gasket_apex_dkms" "2.0" "install_coral.sh"
# Detect running kernel — used both to pull matching headers and to apply
# kernel-version-specific patches if we fall back to google/gasket-driver.
local KVER KMAJ KMIN
@@ -461,7 +483,7 @@ install_gasket_apex_dkms() {
msg_info "$(translate 'Installing build dependencies...')"
apt-get update -qq >>"$LOG_FILE" 2>&1
if ! apt-get install -y git dkms build-essential "proxmox-headers-${KVER}" >>"$LOG_FILE" 2>&1; then
if ! pmx_install_pkg git dkms build-essential "proxmox-headers-${KVER}"; then
msg_error "$(translate 'Error installing build dependencies. Check') ${LOG_FILE}"
exit 1
fi
@@ -478,13 +500,13 @@ install_gasket_apex_dkms() {
# no_llseek was removed in kernel 6.5 — replace with noop_llseek
if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 5 ]]; then
sed -i 's/\.llseek = no_llseek/\.llseek = noop_llseek/' src/gasket_core.c
sed --in-place 's/\.llseek = no_llseek/\.llseek = noop_llseek/' src/gasket_core.c
fi
# MODULE_IMPORT_NS syntax changed to string-literal in 6.13.
# Applying this patch on kernel <6.13 causes a compile error.
if [[ "$KMAJ" -gt 6 ]] || [[ "$KMAJ" -eq 6 && "$KMIN" -ge 13 ]]; then
sed -i 's/^MODULE_IMPORT_NS(DMA_BUF);/MODULE_IMPORT_NS("DMA_BUF");/' src/gasket_page_table.c
sed --in-place 's/^MODULE_IMPORT_NS(DMA_BUF);/MODULE_IMPORT_NS("DMA_BUF");/' src/gasket_page_table.c
fi
msg_ok "$(translate 'Source patched successfully.') (kernel ${KVER})"
@@ -506,11 +528,13 @@ install_gasket_apex_dkms() {
msg_info "$(translate 'Removing previous DKMS source tree...')"
dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Remove previous gasket DKMS entry" "dkms remove gasket/1.0 --all"
if [[ -d "$GASKET_SRC" ]]; then
if ! rm -rf "$GASKET_SRC" 2>>"$LOG_FILE"; then
msg_error "$(translate 'Could not remove previous DKMS tree at') ${GASKET_SRC}. $(translate 'Check') ${LOG_FILE}"
exit 1
fi
pmx_record_execution "Remove previous gasket DKMS source tree" "rm -rf $GASKET_SRC"
fi
msg_ok "$(translate 'Previous DKMS tree cleared.')"
@@ -523,6 +547,7 @@ install_gasket_apex_dkms() {
msg_error "$(translate 'Failed to copy sources into') ${GASKET_SRC}. $(translate 'Check') ${LOG_FILE}"
exit 1
fi
pmx_record_execution "Copy gasket DKMS source tree" "cp -a /tmp/gasket-driver/src/. ${GASKET_SRC}/"
if [[ ! -f "$GASKET_SRC/Makefile" ]]; then
msg_error "$(translate 'Makefile missing in') ${GASKET_SRC} $(translate 'after copy; source tree is incomplete.')"
exit 1
@@ -535,7 +560,7 @@ install_gasket_apex_dkms() {
# MAKE[0] passes ${kernelver} to the Makefile so multi-kernel rebuilds
# (PVE's autoinstall on new kernel installs) target the right headers.
msg_info "$(translate 'Generating dkms.conf...')"
cat > "$GASKET_SRC/dkms.conf" <<'EOF'
pmx_write_file "$GASKET_SRC/dkms.conf" <<'EOF'
PACKAGE_NAME="gasket"
PACKAGE_VERSION="1.0"
BUILT_MODULE_NAME[0]="gasket"
@@ -557,6 +582,7 @@ EOF
msg_error "$(translate 'DKMS add failed. Check') ${LOG_FILE}"
exit 1
fi
pmx_record_execution "Register gasket DKMS module" "dkms add $GASKET_SRC"
msg_ok "$(translate 'DKMS module registered.')"
msg_info "$(translate 'Compiling Coral TPU drivers for current kernel...')"
@@ -565,11 +591,13 @@ EOF
msg_error "$(translate 'DKMS build failed.')"
exit 1
fi
pmx_record_execution "Build gasket DKMS module" "dkms build gasket/1.0 -k $KVER"
if ! dkms install gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then
show_dkms_build_failure
msg_error "$(translate 'DKMS install failed.')"
exit 1
fi
pmx_record_execution "Install gasket DKMS module" "dkms install gasket/1.0 -k $KVER"
msg_ok "$(translate 'Drivers compiled and installed via DKMS.') (source: ${GASKET_SOURCE_USED})"
# Track which feranick release was just installed. Without this, the
@@ -594,7 +622,7 @@ EOF
| sed -E 's/.*"([^"]+)"$/\1/')
if [[ -n "$FERANICK_LATEST" ]]; then
mkdir -p "$CORAL_MARKER_DIR" >>"$LOG_FILE" 2>&1 || true
echo "$FERANICK_LATEST" > "$CORAL_MARKER_FILE" 2>>"$LOG_FILE" || true
printf '%s\n' "$FERANICK_LATEST" | pmx_write_file "$CORAL_MARKER_FILE" || true
echo "[install_coral] Recorded installed gasket-dkms version: $FERANICK_LATEST" >>"$LOG_FILE" 2>&1
else
echo "[install_coral] Could not resolve feranick latest tag — marker not written." >>"$LOG_FILE" 2>&1
@@ -622,6 +650,7 @@ EOF
# ============================================================
install_libedgetpu_runtime() {
pmx_journal_context "install_libedgetpu_runtime" "2.0" "install_coral.sh"
local KEYRING=/etc/apt/keyrings/coral-edgetpu.gpg
local LIST_FILE=/etc/apt/sources.list.d/coral-edgetpu.list
@@ -631,15 +660,24 @@ install_libedgetpu_runtime() {
mkdir -p /etc/apt/keyrings
if [[ ! -s "$KEYRING" ]]; then
if ! curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
| gpg --dearmor -o "$KEYRING" 2>>"$LOG_FILE"; then
local keyring_tmp
local keyring_ready=true
keyring_tmp=$(mktemp /tmp/coral-edgetpu-keyring.XXXXXX)
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg \
| gpg --dearmor >"$keyring_tmp" 2>>"$LOG_FILE" || keyring_ready=false
if $keyring_ready; then
pmx_write_file "$KEYRING" <"$keyring_tmp" || keyring_ready=false
fi
if ! $keyring_ready; then
rm -f "$keyring_tmp"
msg_error "$(translate 'Failed to fetch the Google Coral GPG key. Check') ${LOG_FILE}"
exit 1
fi
rm -f "$keyring_tmp"
chmod 0644 "$KEYRING"
fi
cat > "$LIST_FILE" <<EOF
pmx_write_file "$LIST_FILE" <<EOF
deb [signed-by=${KEYRING}] https://packages.cloud.google.com/apt coral-edgetpu-stable main
EOF
@@ -652,7 +690,7 @@ EOF
# (more heat). We default to -std; users who explicitly want -max can install
# it manually. Either way the udev rules come with the package.
msg_info "$(translate 'Installing Edge TPU runtime (libedgetpu1-std)...')"
if ! apt-get install -y libedgetpu1-std >>"$LOG_FILE" 2>&1; then
if ! pmx_install_pkg libedgetpu1-std; then
msg_error "$(translate 'Failed to install libedgetpu1-std. Check') ${LOG_FILE}"
exit 1
fi
@@ -661,7 +699,9 @@ EOF
# Reload udev so the rules shipped with libedgetpu1-std apply to any USB
# Coral already plugged in (otherwise they would only apply after replug).
udevadm control --reload-rules >/dev/null 2>&1 || true
pmx_record_execution "Reload Coral USB udev rules" "udevadm control --reload-rules"
udevadm trigger --subsystem-match=usb >/dev/null 2>&1 || true
pmx_record_execution "Trigger Coral USB udev rules" "udevadm trigger --subsystem-match=usb"
}
@@ -775,6 +815,7 @@ show_coral_action_menu_if_installed() {
# Idempotent: missing pieces are no-ops, never errors.
# ============================================================
complete_coral_uninstall() {
pmx_journal_context "complete_coral_uninstall" "2.0" "install_coral.sh"
msg_info "$(translate 'Stopping Coral kernel modules...')"
modprobe -r apex 2>>"$LOG_FILE" || true
modprobe -r gasket 2>>"$LOG_FILE" || true
@@ -791,35 +832,43 @@ complete_coral_uninstall() {
while IFS= read -r v; do
[[ -z "$v" ]] && continue
dkms remove -m gasket -v "$v" --all >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Remove gasket DKMS module" "dkms remove -m gasket -v $v --all"
done <<<"$versions"
msg_ok "$(translate 'gasket DKMS entries removed.')"
fi
fi
msg_info "$(translate 'Removing Coral packages...')"
apt-get -y purge gasket-dkms libedgetpu1-std libedgetpu1-max \
>>"$LOG_FILE" 2>&1 || true
if apt-get -y purge gasket-dkms libedgetpu1-std libedgetpu1-max \
>>"$LOG_FILE" 2>&1; then
pmx_record_uninstall "gasket-dkms" "2.0"
pmx_record_uninstall "libedgetpu1-std" "2.0"
pmx_record_uninstall "libedgetpu1-max" "2.0"
fi
apt-get -y autoremove --purge >>"$LOG_FILE" 2>&1 || true
msg_ok "$(translate 'Coral packages purged.')"
# udev rules created by our installer.
rm -f /etc/udev/rules.d/99-coral-apex.rules
pmx_remove_file /etc/udev/rules.d/99-coral-apex.rules
# Restore the upstream udev rule group (set it back to its default
# GROUP="plugdev") in case dkms-postinstall reinstalls gasket-dkms
# later — apex group may not exist next time.
if [[ -f /usr/lib/udev/rules.d/60-gasket-dkms.rules ]]; then
sed -i 's/GROUP="apex"/GROUP="plugdev"/g' \
/usr/lib/udev/rules.d/60-gasket-dkms.rules || true
pmx_edit_file /usr/lib/udev/rules.d/60-gasket-dkms.rules \
's/GROUP="apex"/GROUP="plugdev"/g' || true
fi
udevadm control --reload-rules
pmx_record_execution "Reload udev rules after Coral removal" "udevadm control --reload-rules"
udevadm trigger --subsystem-match=apex >/dev/null 2>&1 || true
pmx_record_execution "Trigger udev rules after Coral removal" "udevadm trigger --subsystem-match=apex"
# Apex system group: only remove if no one else is using it.
if getent group apex >/dev/null 2>&1; then
local apex_members
apex_members=$(getent group apex | cut -d: -f4)
if [[ -z "$apex_members" ]]; then
groupdel apex >>"$LOG_FILE" 2>&1 || true
pmx_apply_setting "group:apex" "getent group apex 2>/dev/null || true" \
groupdel apex || true
msg_ok "$(translate 'apex group removed.')"
else
msg_warn "$(translate 'apex group still has members; left in place:') $apex_members"
@@ -827,16 +876,19 @@ complete_coral_uninstall() {
fi
# Google Coral APT repo + keyring (only added during USB install).
rm -f /etc/apt/sources.list.d/coral-edgetpu.list \
local coral_file
for coral_file in \
/etc/apt/sources.list.d/coral-edgetpu.list \
/etc/apt/sources.list.d/coral-cloud.list \
/usr/share/keyrings/coral-edgetpu-archive-keyring.gpg \
/etc/apt/trusted.gpg.d/coral-edgetpu-archive-keyring.gpg \
2>/dev/null || true
/etc/apt/trusted.gpg.d/coral-edgetpu-archive-keyring.gpg; do
pmx_remove_file "$coral_file" || true
done
# Drop the gasket-dkms version marker written by the install path.
# Leaving it around after a full uninstall would let the Monitor
# claim a fictional driver version on the next reboot.
rm -f /var/lib/proxmenux/coral_gasket_version 2>/dev/null || true
pmx_remove_file /var/lib/proxmenux/coral_gasket_version || true
# Update component status if utils.sh exposes the helper (older
# ProxMenux releases didn't have it; uninstall must still work).
+9 -2
View File
@@ -51,6 +51,10 @@ if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
fi
load_language
initialize_cache
@@ -161,6 +165,7 @@ suggest_gpu_passthrough_if_needed() {
# ==========================================================
add_udev_rule_for_coral_usb() {
pmx_journal_context "add_udev_rule_for_coral_usb" "1.5" "install_coral_lxc.sh"
RULE_FILE="/etc/udev/rules.d/99-coral-usb.rules"
RULE_CONTENT='# Coral USB Accelerator
SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666", TAG+="uaccess", SYMLINK+="coral"
@@ -168,14 +173,16 @@ SUBSYSTEM=="usb", ATTRS{idVendor}=="18d1", ATTRS{idProduct}=="9302", MODE="0666"
SUBSYSTEM=="usb", ATTRS{idVendor}=="1a6e", ATTRS{idProduct}=="089a", MODE="0666", TAG+="uaccess", SYMLINK+="coral"'
if [[ ! -f "$RULE_FILE" ]]; then
echo "$RULE_CONTENT" > "$RULE_FILE"
printf '%s\n' "$RULE_CONTENT" | pmx_write_file "$RULE_FILE"
udevadm control --reload-rules && udevadm trigger
pmx_record_execution "reload Coral USB udev rules" "udevadm control --reload-rules && udevadm trigger"
msg_ok "$(translate 'Udev rules for Coral USB devices added and rules reloaded.')"
elif ! grep -q "18d1.*9302\|1a6e.*089a" "$RULE_FILE"; then
# Append (>>) instead of overwriting (>) so any user-authored
# rules in this file survive.
printf '\n%s\n' "$RULE_CONTENT" >> "$RULE_FILE"
printf '\n%s\n' "$RULE_CONTENT" | pmx_append_file "$RULE_FILE"
udevadm control --reload-rules && udevadm trigger
pmx_record_execution "reload Coral USB udev rules" "udevadm control --reload-rules && udevadm trigger"
msg_ok "$(translate 'Udev rules for Coral USB devices appended and rules reloaded.')"
else
msg_ok "$(translate 'Udev rules for Coral USB devices already exist.')"
+11 -3
View File
@@ -24,6 +24,10 @@ if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
fi
if [[ -f "$COMMON_FUNC" ]]; then
source "$COMMON_FUNC"
fi
@@ -73,9 +77,10 @@ check_intel_gpu_tools_installed() {
# Install intel-gpu-tools
# ==========================================================
install_intel_gpu_tools() {
pmx_journal_context "install_intel_gpu_tools" "1.0" "intel_gpu_tools.sh"
msg_info "$(translate 'Installing intel-gpu-tools...')"
if apt-get install -y intel-gpu-tools >>"$LOG_FILE" 2>&1; then
if pmx_install_pkg intel-gpu-tools >>"$LOG_FILE" 2>&1; then
msg_ok "$(translate 'intel-gpu-tools installed successfully')"
# Get installed version
@@ -97,9 +102,11 @@ install_intel_gpu_tools() {
# Uninstall intel-gpu-tools
# ==========================================================
uninstall_intel_gpu_tools() {
pmx_journal_context "uninstall_intel_gpu_tools" "1.0" "intel_gpu_tools.sh"
msg_info "$(translate 'Uninstalling intel-gpu-tools...')"
if apt-get remove -y intel-gpu-tools >>"$LOG_FILE" 2>&1; then
pmx_record_uninstall "intel-gpu-tools"
msg_ok "$(translate 'intel-gpu-tools uninstalled successfully')"
if type update_component_status &>/dev/null; then
@@ -116,6 +123,7 @@ uninstall_intel_gpu_tools() {
# Main execution
# ==========================================================
main() {
pmx_journal_context "main" "1.0" "intel_gpu_tools.sh"
# Show ProxMenux logo and title
show_proxmenux_logo
msg_title "$(translate "$SCRIPT_TITLE")"
@@ -149,7 +157,7 @@ main() {
if [[ -n "$available_version" && "$available_version" != "$INTEL_GPU_TOOLS_VERSION" ]]; then
msg_ok "$(translate 'A newer version is available:') $available_version"
if apt-get install -y intel-gpu-tools >>"$LOG_FILE" 2>&1; then
if pmx_install_pkg intel-gpu-tools >>"$LOG_FILE" 2>&1; then
INTEL_GPU_TOOLS_VERSION="$available_version"
msg_ok "$(translate 'intel-gpu-tools updated to') $INTEL_GPU_TOOLS_VERSION"
@@ -219,4 +227,4 @@ if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
exit $?
fi
main
fi
fi
+67 -26
View File
@@ -53,6 +53,9 @@ export COMPONENTS_STATUS_FILE
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
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
@@ -134,6 +137,7 @@ check_gpu_not_in_vm_passthrough() {
}
check_stale_vfio_config_for_nvidia() {
pmx_journal_context "check_stale_vfio_config_for_nvidia" "1.3" "nvidia_installer.sh"
local vfio_conf="/etc/modprobe.d/vfio.conf"
[[ ! -f "$vfio_conf" ]] && return 0
@@ -173,8 +177,10 @@ check_stale_vfio_config_for_nvidia() {
if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \
&& _clean_vfio_conf_ids "${legacy_ids[@]}"; then
pmx_record_execution "Clean stale NVIDIA VFIO IDs" "_clean_vfio_conf_ids ${legacy_ids[*]}"
msg_info "$(translate 'Rebuilding initramfs after vfio.conf cleanup...')"
update-initramfs -u >/dev/null 2>&1 || true
pmx_record_execution "Rebuild initramfs after NVIDIA VFIO cleanup" "update-initramfs -u"
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"
@@ -510,6 +516,7 @@ offer_lxc_updates_if_any() {
# System preparation (repos, headers, etc.)
# ==========================================================
ensure_repos_and_headers() {
pmx_journal_context "ensure_repos_and_headers" "1.3" "nvidia_installer.sh"
# Bootstrap APT repos FIRST. On a fresh Proxmox install the
# pve-no-subscription / debian repos aren't configured by default
# → `pve-headers-$(uname -r)` and `build-essential` come back as
@@ -537,9 +544,9 @@ ensure_repos_and_headers() {
if ! dpkg -s "pve-headers-$kver" >/dev/null 2>&1 && \
! dpkg -s "proxmox-headers-$kver" >/dev/null 2>&1; then
apt-get install -y "pve-headers-$kver" "proxmox-headers-$kver" build-essential dkms >>"$LOG_FILE" 2>&1 || true
pmx_install_pkg "pve-headers-$kver" "proxmox-headers-$kver" build-essential dkms || true
else
apt-get install -y build-essential dkms >>"$LOG_FILE" 2>&1 || true
pmx_install_pkg build-essential dkms || true
fi
msg_ok "$(translate 'Kernel headers and build tools verified.')" | tee -a "$screen_capture"
@@ -553,14 +560,18 @@ _nouveau_legacy_file_is_proxmenux_shape() {
}
_nouveau_state_set() {
pmx_journal_context "_nouveau_state_set" "1.3" "nvidia_installer.sh"
local key="$1"
mkdir -p "$(dirname "$NVIDIA_NOUVEAU_STATE")"
touch "$NVIDIA_NOUVEAU_STATE"
if [[ ! -f "$NVIDIA_NOUVEAU_STATE" ]]; then
pmx_write_file "$NVIDIA_NOUVEAU_STATE" </dev/null
fi
grep -qFx "${key}=1" "$NVIDIA_NOUVEAU_STATE" 2>/dev/null \
|| echo "${key}=1" >> "$NVIDIA_NOUVEAU_STATE"
|| printf '%s\n' "${key}=1" | pmx_append_file "$NVIDIA_NOUVEAU_STATE"
}
restore_nouveau_after_uninstall() {
pmx_journal_context "restore_nouveau_after_uninstall" "1.3" "nvidia_installer.sh"
local remove_global_line=false
if [[ -f "$NVIDIA_NOUVEAU_STATE" ]] \
@@ -572,29 +583,30 @@ restore_nouveau_after_uninstall() {
# 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"
pmx_remove_file "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST"
remove_global_line=true
fi
rm -f "$NVIDIA_NOUVEAU_BLACKLIST"
pmx_remove_file "$NVIDIA_NOUVEAU_BLACKLIST"
if $remove_global_line && [[ -f "$NVIDIA_GLOBAL_BLACKLIST" ]]; then
sed -i '/^blacklist nouveau$/d' "$NVIDIA_GLOBAL_BLACKLIST"
pmx_edit_file "$NVIDIA_GLOBAL_BLACKLIST" '/^blacklist nouveau$/d'
fi
rm -f "$NVIDIA_NOUVEAU_STATE"
pmx_remove_file "$NVIDIA_NOUVEAU_STATE"
}
blacklist_nouveau() {
pmx_journal_context "blacklist_nouveau" "1.3" "nvidia_installer.sh"
msg_info "$(translate 'Blacklisting nouveau driver...')"
local legacy_owned=false
if _nouveau_legacy_file_is_proxmenux_shape; then
rm -f "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST"
pmx_remove_file "$NVIDIA_NOUVEAU_LEGACY_BLACKLIST"
legacy_owned=true
_nouveau_state_set "legacy_migrated"
fi
if ! grep -q '^blacklist nouveau$' "$NVIDIA_GLOBAL_BLACKLIST" 2>/dev/null; then
echo "blacklist nouveau" >> "$NVIDIA_GLOBAL_BLACKLIST"
printf '%s\n' "blacklist nouveau" | pmx_append_file "$NVIDIA_GLOBAL_BLACKLIST"
_nouveau_state_set "blacklist_conf_line_added"
elif $legacy_owned; then
# The legacy ProxMenux file proves ownership of the companion line.
@@ -602,7 +614,7 @@ blacklist_nouveau() {
fi
# ProxMenux-owned file: uninstall can now remove only what we created.
cat > "$NVIDIA_NOUVEAU_BLACKLIST" <<'EOF'
pmx_write_file "$NVIDIA_NOUVEAU_BLACKLIST" <<'EOF'
# Managed by ProxMenux NVIDIA installer.
blacklist nouveau
options nouveau modeset=0
@@ -637,8 +649,9 @@ EOF
}
ensure_modules_config() {
pmx_journal_context "ensure_modules_config" "1.3" "nvidia_installer.sh"
msg_info "$(translate 'Configuring NVIDIA modules...')"
cat > /etc/modules-load.d/nvidia-vfio.conf <<'EOF'
pmx_write_file /etc/modules-load.d/nvidia-vfio.conf <<'EOF'
nvidia
nvidia_uvm
EOF
@@ -646,6 +659,7 @@ EOF
}
stop_and_disable_nvidia_services() {
pmx_journal_context "stop_and_disable_nvidia_services" "1.3" "nvidia_installer.sh"
local services=(
"nvidia-persistenced.service"
"nvidia-persistenced"
@@ -666,12 +680,7 @@ stop_and_disable_nvidia_services() {
msg_info "$(translate 'Stopping and disabling NVIDIA services...')"
for service in "${services[@]}"; do
if systemctl is-active --quiet "$service" 2>/dev/null; then
systemctl stop "$service" >/dev/null 2>&1 || true
fi
if systemctl is-enabled --quiet "$service" 2>/dev/null; then
systemctl disable "$service" >/dev/null 2>&1 || true
fi
pmx_disable_service "$service" || true
done
sleep 2
@@ -711,26 +720,36 @@ unload_nvidia_modules() {
}
complete_nvidia_uninstall() {
pmx_journal_context "complete_nvidia_uninstall" "1.3" "nvidia_installer.sh"
stop_and_disable_nvidia_services
unload_nvidia_modules
if command -v nvidia-uninstall >/dev/null 2>&1; then
msg_info "$(translate 'Running NVIDIA uninstaller...')"
nvidia-uninstall --silent >>"$LOG_FILE" 2>&1 || true
local uninstall_rc=0
nvidia-uninstall --silent >>"$LOG_FILE" 2>&1 || uninstall_rc=$?
pmx_record_execution "Run NVIDIA uninstaller" "nvidia-uninstall --silent"
if [[ "$uninstall_rc" -eq 0 ]]; then
pmx_record_uninstall "NVIDIA driver" "1.3"
fi
msg_ok "$(translate 'NVIDIA uninstaller completed.')"
fi
msg_ok "$(translate 'NVIDIA uninstallation steps completed.')" | tee -a "$screen_capture"
cleanup_nvidia_dkms
msg_info "$(translate 'Removing NVIDIA packages...')"
apt-get -y purge 'nvidia-*' 'libnvidia-*' 'cuda-*' 'libcudnn*' >>"$LOG_FILE" 2>&1 || true
if apt-get -y purge 'nvidia-*' 'libnvidia-*' 'cuda-*' 'libcudnn*' >>"$LOG_FILE" 2>&1; then
pmx_record_uninstall "nvidia-* libnvidia-* cuda-* libcudnn*" "1.3"
fi
apt-get -y autoremove --purge >>"$LOG_FILE" 2>&1 || true
apt-get -y autoclean >>"$LOG_FILE" 2>&1 || true
rm -f /etc/modules-load.d/nvidia-vfio.conf
rm -f /etc/udev/rules.d/70-nvidia.rules
rm -rf /usr/lib/modprobe.d/nvidia*.conf
rm -rf /etc/modprobe.d/nvidia*.conf
pmx_remove_file /etc/modules-load.d/nvidia-vfio.conf
pmx_remove_file /etc/udev/rules.d/70-nvidia.rules
local nvidia_conf
for nvidia_conf in /usr/lib/modprobe.d/nvidia*.conf /etc/modprobe.d/nvidia*.conf; do
[[ -f "$nvidia_conf" ]] && pmx_remove_file "$nvidia_conf"
done
restore_nouveau_after_uninstall
if [[ -d "$NVIDIA_WORKDIR" ]]; then
@@ -745,6 +764,7 @@ complete_nvidia_uninstall() {
}
cleanup_nvidia_dkms() {
pmx_journal_context "cleanup_nvidia_dkms" "1.3" "nvidia_installer.sh"
local versions
versions=$(dkms status 2>/dev/null | awk -F, '/nvidia/ {gsub(/ /,"",$2); print $2}' || true)
@@ -754,6 +774,7 @@ cleanup_nvidia_dkms() {
while IFS= read -r ver; do
[[ -z "$ver" ]] && continue
dkms remove -m nvidia -v "$ver" --all >/dev/null 2>&1 || true
pmx_record_execution "Remove NVIDIA DKMS entry" "dkms remove -m nvidia -v $ver --all"
done <<< "$versions"
msg_ok "$(translate 'NVIDIA DKMS entries removed.')"
}
@@ -1365,6 +1386,7 @@ download_nvidia_installer() {
# Installation / uninstallation
# ==========================================================
run_nvidia_installer() {
pmx_journal_context "run_nvidia_installer" "1.3" "nvidia_installer.sh"
local installer="$1"
msg_info2 "$(translate 'Starting NVIDIA installer. This may take several minutes...')"
@@ -1377,7 +1399,9 @@ run_nvidia_installer() {
if [[ "${NOUVEAU_STILL_LOADED:-false}" == "true" ]]; then
msg_info "$(translate 'Rebuilding initramfs to apply nouveau blacklist before installation...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Rebuild initramfs for NVIDIA installation" "update-initramfs -u -k all"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Refresh Proxmox boot configuration for NVIDIA installation" "proxmox-boot-tool refresh"
# Try one more time to unload nouveau after initramfs rebuild
modprobe -r nouveau 2>/dev/null || true
sleep 1
@@ -1406,6 +1430,7 @@ run_nvidia_installer() {
2>&1 | tee -a "$LOG_FILE"
local rc=${PIPESTATUS[0]}
echo "" >>"$LOG_FILE"
pmx_record_execution "Run NVIDIA driver installer" "sh $installer --no-questions --ui=none --disable-nouveau --no-nouveau-check --dkms"
rm -rf "$tmp_extract_dir"
@@ -1415,6 +1440,7 @@ run_nvidia_installer() {
return 1
fi
pmx_record_install "NVIDIA driver" "${DRIVER_VERSION:-1.3}"
msg_ok "$(translate 'NVIDIA driver installed successfully.')" | tee -a "$screen_capture"
return 0
}
@@ -1424,16 +1450,19 @@ remove_nvidia_driver() {
}
install_udev_rules_and_persistenced() {
pmx_journal_context "install_udev_rules_and_persistenced" "1.3" "nvidia_installer.sh"
msg_info "$(translate 'Installing NVIDIA udev rules and persistence service...')"
cat >/etc/udev/rules.d/70-nvidia.rules <<'EOF'
pmx_write_file /etc/udev/rules.d/70-nvidia.rules <<'EOF'
# /etc/udev/rules.d/70-nvidia.rules
KERNEL=="nvidia", RUN+="/bin/bash -c '/usr/bin/nvidia-smi -L'"
KERNEL=="nvidia_uvm", RUN+="/bin/bash -c '/usr/bin/nvidia-modprobe -c0 -u'"
EOF
udevadm control --reload-rules
pmx_record_execution "Reload NVIDIA udev rules" "udevadm control --reload-rules"
udevadm trigger --subsystem-match=drm --subsystem-match=pci || true
pmx_record_execution "Trigger NVIDIA udev rules" "udevadm trigger --subsystem-match=drm --subsystem-match=pci"
ensure_workdir
cd "$NVIDIA_WORKDIR" || return 1
@@ -1451,13 +1480,19 @@ EOF
if [[ -d nvidia-persistenced/init ]]; then
cd nvidia-persistenced/init || return 1
./install.sh >>"$LOG_FILE" 2>&1 || true
local persistenced_rc=0
./install.sh >>"$LOG_FILE" 2>&1 || persistenced_rc=$?
pmx_record_execution "Install NVIDIA persistence service" "$NVIDIA_WORKDIR/nvidia-persistenced/init/install.sh"
if [[ "$persistenced_rc" -eq 0 ]]; then
pmx_record_install "nvidia-persistenced" "$NVIDIA_PERSISTENCED_TAG"
fi
fi
msg_ok "$(translate 'NVIDIA udev rules and persistence service installed.')" | tee -a "$screen_capture"
}
apply_nvidia_patch_if_needed() {
pmx_journal_context "apply_nvidia_patch_if_needed" "1.3" "nvidia_installer.sh"
# NVIDIA_PATCH_AUTO=yes|no skips the yes/no prompt for non-interactive
# callers; unset preserves the interactive menu behavior.
case "${NVIDIA_PATCH_AUTO:-}" in
@@ -1504,6 +1539,7 @@ apply_nvidia_patch_if_needed() {
if [[ -x nvidia-patch/patch.sh ]]; then
cd nvidia-patch || return 1
./patch.sh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Apply NVIDIA patch" "$NVIDIA_WORKDIR/nvidia-patch/patch.sh"
msg_ok "$(translate 'NVIDIA patch applied - check README for supported versions.')"
update_component_status "nvidia_driver" "installed" "$CURRENT_DRIVER_VERSION" "gpu" '{"patched":true}'
else
@@ -1739,6 +1775,7 @@ show_version_menu() {
# Main flow
# ==========================================================
main() {
pmx_journal_context "main" "1.3" "nvidia_installer.sh"
# Rotate the previous run's log instead of truncating — when the
# current install fails, the user can compare against the previous
# attempt to see what changed. Audit Tier 7 — log truncation.
@@ -1846,7 +1883,9 @@ main() {
msg_info "$(translate 'Updating initramfs for all kernels...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Rebuild initramfs after NVIDIA installation" "update-initramfs -u -k all"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Refresh Proxmox boot configuration after NVIDIA installation" "proxmox-boot-tool refresh"
msg_ok "$(translate 'initramfs updated.')"
msg_info2 "$(translate 'Checking NVIDIA driver status with nvidia-smi')"
@@ -1889,7 +1928,9 @@ main() {
msg_info "$(translate 'Updating initramfs for all kernels...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Rebuild initramfs after NVIDIA removal" "update-initramfs -u -k all"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "Refresh Proxmox boot configuration after NVIDIA removal" "proxmox-boot-tool refresh"
msg_ok "$(translate 'initramfs updated.')"
restart_prompt
+61 -20
View File
@@ -57,6 +57,9 @@ screen_capture="/tmp/proxmenux_gpu_switch_mode_screen_$$.txt"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
fi
# Both helper libraries are required for the SR-IOV guard and the audio
# orphan cascade to work. Surface a loud warning if neither path resolves
# — the previous behaviour evaluated `declare -F` later and silently
@@ -112,11 +115,12 @@ _set_title() {
}
_add_line_if_missing() {
pmx_journal_context "_add_line_if_missing" "1.1" "switch_gpu_mode.sh"
local line="$1"
local file="$2"
touch "$file"
[[ -f "$file" ]] || pmx_write_file "$file" < /dev/null
if ! grep -qFx "$line" "$file" 2>/dev/null; then
echo "$line" >>"$file"
echo "$line" | pmx_append_file "$file"
HOST_CONFIG_CHANGED=true
fi
}
@@ -199,22 +203,28 @@ _read_vfio_ids() {
}
_write_vfio_ids() {
pmx_journal_context "_write_vfio_ids" "1.1" "switch_gpu_mode.sh"
local -a ids=("$@")
local vfio_conf="/etc/modprobe.d/vfio.conf"
touch "$vfio_conf"
local vfio_tmp
vfio_tmp=$(mktemp)
[[ -f "$vfio_conf" ]] && cat "$vfio_conf" > "$vfio_tmp"
local current_line new_line ids_str
current_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
sed -i '/^options vfio-pci ids=/d' "$vfio_conf"
sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next"
mv "${vfio_tmp}.next" "$vfio_tmp"
if [[ ${#ids[@]} -gt 0 ]]; then
ids_str=$(IFS=','; echo "${ids[*]}")
new_line="options vfio-pci ids=${ids_str} disable_vga=1"
echo "$new_line" >>"$vfio_conf"
echo "$new_line" >>"$vfio_tmp"
[[ "$current_line" != "$new_line" ]] && HOST_CONFIG_CHANGED=true
else
[[ -n "$current_line" ]] && HOST_CONFIG_CHANGED=true
fi
pmx_write_file "$vfio_conf" < "$vfio_tmp"
rm -f "$vfio_tmp"
}
_contains_in_array() {
@@ -228,6 +238,7 @@ _contains_in_array() {
}
_remove_gpu_blacklist() {
pmx_journal_context "_remove_gpu_blacklist" "1.1" "switch_gpu_mode.sh"
local gpu_type="$1"
local blacklist_file="/etc/modprobe.d/blacklist.conf"
[[ ! -f "$blacklist_file" ]] && return
@@ -241,12 +252,13 @@ _remove_gpu_blacklist() {
;;
amd)
grep -qE '^blacklist (radeon|amdgpu)$' "$blacklist_file" 2>/dev/null && changed=true
sed -i '/^blacklist radeon$/d' "$blacklist_file"
sed -i '/^blacklist amdgpu$/d' "$blacklist_file"
pmx_edit_file "$blacklist_file" \
-e '/^blacklist radeon$/d' \
-e '/^blacklist amdgpu$/d'
;;
intel)
grep -qE '^blacklist i915$' "$blacklist_file" 2>/dev/null && changed=true
sed -i '/^blacklist i915$/d' "$blacklist_file"
pmx_edit_file "$blacklist_file" '/^blacklist i915$/d'
;;
esac
$changed && HOST_CONFIG_CHANGED=true
@@ -293,13 +305,15 @@ _add_amd_softdep() {
}
_remove_amd_softdep() {
pmx_journal_context "_remove_amd_softdep" "1.1" "switch_gpu_mode.sh"
local vfio_conf="/etc/modprobe.d/vfio.conf"
[[ ! -f "$vfio_conf" ]] && return
local changed=false
grep -qE '^softdep (radeon|amdgpu|snd_hda_intel) pre: vfio-pci$' "$vfio_conf" 2>/dev/null && changed=true
sed -i '/^softdep radeon pre: vfio-pci$/d' "$vfio_conf"
sed -i '/^softdep amdgpu pre: vfio-pci$/d' "$vfio_conf"
sed -i '/^softdep snd_hda_intel pre: vfio-pci$/d' "$vfio_conf"
pmx_edit_file "$vfio_conf" \
-e '/^softdep radeon pre: vfio-pci$/d' \
-e '/^softdep amdgpu pre: vfio-pci$/d' \
-e '/^softdep snd_hda_intel pre: vfio-pci$/d'
$changed && HOST_CONFIG_CHANGED=true
$changed
}
@@ -319,6 +333,7 @@ _add_vfio_modules() {
}
_remove_vfio_modules_if_unused() {
pmx_journal_context "_remove_vfio_modules_if_unused" "1.1" "switch_gpu_mode.sh"
local vfio_count
vfio_count=$(_read_vfio_ids | wc -l | tr -d '[:space:]')
[[ "$vfio_count" != "0" ]] && return 1
@@ -330,10 +345,11 @@ _remove_vfio_modules_if_unused() {
[[ ! -f "$modules_file" ]] && return 1
local had_any=false
grep -qE '^vfio$|^vfio_iommu_type1$|^vfio_pci$|^vfio_virqfd$' "$modules_file" 2>/dev/null && had_any=true
sed -i '/^vfio$/d' "$modules_file"
sed -i '/^vfio_iommu_type1$/d' "$modules_file"
sed -i '/^vfio_pci$/d' "$modules_file"
sed -i '/^vfio_virqfd$/d' "$modules_file"
pmx_edit_file "$modules_file" \
-e '/^vfio$/d' \
-e '/^vfio_iommu_type1$/d' \
-e '/^vfio_pci$/d' \
-e '/^vfio_virqfd$/d'
if $had_any; then
HOST_CONFIG_CHANGED=true
return 0
@@ -422,6 +438,7 @@ _selected_gpu_current_mode() {
}
check_stale_vfio_config_switch_mode() {
pmx_journal_context "check_stale_vfio_config_switch_mode" "1.1" "switch_gpu_mode.sh"
local vfio_conf="/etc/modprobe.d/vfio.conf"
[[ ! -f "$vfio_conf" ]] && return 0
@@ -461,8 +478,10 @@ check_stale_vfio_config_switch_mode() {
if declare -F _clean_vfio_conf_ids >/dev/null 2>&1 \
&& _clean_vfio_conf_ids "${legacy_ids[@]}"; then
pmx_record_execution "remove stale VFIO IDs" "_clean_vfio_conf_ids ${legacy_ids[*]}"
msg_info "$(translate 'Rebuilding initramfs after vfio.conf cleanup...')"
update-initramfs -u >/dev/null 2>&1 || true
pmx_record_execution "rebuild initramfs" "update-initramfs -u"
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.')" \
@@ -1056,14 +1075,28 @@ apply_vm_action_for_lxc_mode() {
}
_register_iommu_tool() {
pmx_journal_context "_register_iommu_tool" "1.1" "switch_gpu_mode.sh"
local tools_json="${BASE_DIR:-/usr/local/share/proxmenux}/installed_tools.json"
command -v jq >/dev/null 2>&1 || return 0
[[ -f "$tools_json" ]] || echo "{}" > "$tools_json"
jq '.vfio_iommu=true' "$tools_json" > "$tools_json.tmp" \
&& mv "$tools_json.tmp" "$tools_json" || true
local tools_tmp
tools_tmp=$(mktemp)
if [[ -f "$tools_json" ]]; then
jq '.vfio_iommu=true' "$tools_json" > "$tools_tmp" \
&& pmx_write_file "$tools_json" < "$tools_tmp" || true
else
printf '{}\n' > "$tools_tmp"
if jq '.vfio_iommu=true' "$tools_tmp" > "${tools_tmp}.next"; then
mv "${tools_tmp}.next" "$tools_tmp"
else
rm -f "${tools_tmp}.next"
fi
pmx_write_file "$tools_json" < "$tools_tmp" || true
fi
rm -f "$tools_tmp" "${tools_tmp}.next"
}
_enable_iommu_cmdline() {
pmx_journal_context "_enable_iommu_cmdline" "1.1" "switch_gpu_mode.sh"
local cpu_vendor
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
@@ -1082,14 +1115,16 @@ _enable_iommu_cmdline() {
if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then
if ! grep -q "$iommu_param" "$cmdline_file"; then
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
fi
elif [[ -f "$grub_file" ]]; then
if ! grep -q "$iommu_param" "$grub_file"; then
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
update-grub >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "update GRUB configuration" "update-grub"
fi
else
return 1
@@ -1098,6 +1133,7 @@ _enable_iommu_cmdline() {
}
switch_to_vm_mode() {
pmx_journal_context "switch_to_vm_mode" "1.1" "switch_gpu_mode.sh"
detect_affected_lxc_for_selected
prompt_lxc_action_for_vm_mode
@@ -1177,7 +1213,9 @@ switch_to_vm_mode() {
if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then
msg_info "$(translate 'Updating initramfs (this may take a minute)...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1
pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture"
fi
}
@@ -1196,6 +1234,7 @@ _type_has_remaining_vfio_ids() {
}
switch_to_lxc_mode() {
pmx_journal_context "switch_to_lxc_mode" "1.1" "switch_gpu_mode.sh"
collect_selected_iommu_ids
detect_affected_vms_for_selected
prompt_vm_action_for_lxc_mode
@@ -1256,7 +1295,9 @@ switch_to_lxc_mode() {
if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then
msg_info "$(translate 'Updating initramfs (this may take a minute)...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1
pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture"
fi
}
+61 -20
View File
@@ -32,6 +32,9 @@ screen_capture="/tmp/proxmenux_gpu_switch_mode_screen_$$.txt"
if [[ -f "$UTILS_FILE" ]]; then
source "$UTILS_FILE"
fi
if [[ -f "$BASE_DIR/scripts/global/pmx_journal.sh" ]]; then
source "$BASE_DIR/scripts/global/pmx_journal.sh"
fi
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
@@ -92,11 +95,12 @@ _set_title() {
}
_add_line_if_missing() {
pmx_journal_context "_add_line_if_missing" "1.1" "switch_gpu_mode_direct.sh"
local line="$1"
local file="$2"
touch "$file"
[[ -f "$file" ]] || pmx_write_file "$file" < /dev/null
if ! grep -qFx "$line" "$file" 2>/dev/null; then
echo "$line" >>"$file"
echo "$line" | pmx_append_file "$file"
HOST_CONFIG_CHANGED=true
fi
}
@@ -177,22 +181,28 @@ _read_vfio_ids() {
}
_write_vfio_ids() {
pmx_journal_context "_write_vfio_ids" "1.1" "switch_gpu_mode_direct.sh"
local -a ids=("$@")
local vfio_conf="/etc/modprobe.d/vfio.conf"
touch "$vfio_conf"
local vfio_tmp
vfio_tmp=$(mktemp)
[[ -f "$vfio_conf" ]] && cat "$vfio_conf" > "$vfio_tmp"
local current_line new_line ids_str
current_line=$(grep "^options vfio-pci ids=" "$vfio_conf" 2>/dev/null | head -1)
sed -i '/^options vfio-pci ids=/d' "$vfio_conf"
sed '/^options vfio-pci ids=/d' "$vfio_tmp" > "${vfio_tmp}.next"
mv "${vfio_tmp}.next" "$vfio_tmp"
if [[ ${#ids[@]} -gt 0 ]]; then
ids_str=$(IFS=','; echo "${ids[*]}")
new_line="options vfio-pci ids=${ids_str} disable_vga=1"
echo "$new_line" >>"$vfio_conf"
echo "$new_line" >>"$vfio_tmp"
[[ "$current_line" != "$new_line" ]] && HOST_CONFIG_CHANGED=true
else
[[ -n "$current_line" ]] && HOST_CONFIG_CHANGED=true
fi
pmx_write_file "$vfio_conf" < "$vfio_tmp"
rm -f "$vfio_tmp"
}
_contains_in_array() {
@@ -206,6 +216,7 @@ _contains_in_array() {
}
_remove_gpu_blacklist() {
pmx_journal_context "_remove_gpu_blacklist" "1.1" "switch_gpu_mode_direct.sh"
local gpu_type="$1"
local blacklist_file="/etc/modprobe.d/blacklist.conf"
[[ ! -f "$blacklist_file" ]] && return
@@ -219,12 +230,13 @@ _remove_gpu_blacklist() {
;;
amd)
grep -qE '^blacklist (radeon|amdgpu)$' "$blacklist_file" 2>/dev/null && changed=true
sed -i '/^blacklist radeon$/d' "$blacklist_file"
sed -i '/^blacklist amdgpu$/d' "$blacklist_file"
pmx_edit_file "$blacklist_file" \
-e '/^blacklist radeon$/d' \
-e '/^blacklist amdgpu$/d'
;;
intel)
grep -qE '^blacklist i915$' "$blacklist_file" 2>/dev/null && changed=true
sed -i '/^blacklist i915$/d' "$blacklist_file"
pmx_edit_file "$blacklist_file" '/^blacklist i915$/d'
;;
esac
$changed && HOST_CONFIG_CHANGED=true
@@ -271,13 +283,15 @@ _add_amd_softdep() {
}
_remove_amd_softdep() {
pmx_journal_context "_remove_amd_softdep" "1.1" "switch_gpu_mode_direct.sh"
local vfio_conf="/etc/modprobe.d/vfio.conf"
[[ ! -f "$vfio_conf" ]] && return
local changed=false
grep -qE '^softdep (radeon|amdgpu|snd_hda_intel) pre: vfio-pci$' "$vfio_conf" 2>/dev/null && changed=true
sed -i '/^softdep radeon pre: vfio-pci$/d' "$vfio_conf"
sed -i '/^softdep amdgpu pre: vfio-pci$/d' "$vfio_conf"
sed -i '/^softdep snd_hda_intel pre: vfio-pci$/d' "$vfio_conf"
pmx_edit_file "$vfio_conf" \
-e '/^softdep radeon pre: vfio-pci$/d' \
-e '/^softdep amdgpu pre: vfio-pci$/d' \
-e '/^softdep snd_hda_intel pre: vfio-pci$/d'
$changed && HOST_CONFIG_CHANGED=true
$changed
}
@@ -297,6 +311,7 @@ _add_vfio_modules() {
}
_remove_vfio_modules_if_unused() {
pmx_journal_context "_remove_vfio_modules_if_unused" "1.1" "switch_gpu_mode_direct.sh"
local vfio_count
vfio_count=$(_read_vfio_ids | wc -l | tr -d '[:space:]')
[[ "$vfio_count" != "0" ]] && return 1
@@ -308,10 +323,11 @@ _remove_vfio_modules_if_unused() {
[[ ! -f "$modules_file" ]] && return 1
local had_any=false
grep -qE '^vfio$|^vfio_iommu_type1$|^vfio_pci$|^vfio_virqfd$' "$modules_file" 2>/dev/null && had_any=true
sed -i '/^vfio$/d' "$modules_file"
sed -i '/^vfio_iommu_type1$/d' "$modules_file"
sed -i '/^vfio_pci$/d' "$modules_file"
sed -i '/^vfio_virqfd$/d' "$modules_file"
pmx_edit_file "$modules_file" \
-e '/^vfio$/d' \
-e '/^vfio_iommu_type1$/d' \
-e '/^vfio_pci$/d' \
-e '/^vfio_virqfd$/d'
if $had_any; then
HOST_CONFIG_CHANGED=true
return 0
@@ -844,14 +860,28 @@ apply_vm_action_for_lxc_mode() {
# Switch Mode Functions
# ==========================================================
_register_iommu_tool() {
pmx_journal_context "_register_iommu_tool" "1.1" "switch_gpu_mode_direct.sh"
local tools_json="${BASE_DIR:-/usr/local/share/proxmenux}/installed_tools.json"
command -v jq >/dev/null 2>&1 || return 0
[[ -f "$tools_json" ]] || echo "{}" > "$tools_json"
jq '.vfio_iommu=true' "$tools_json" > "$tools_json.tmp" \
&& mv "$tools_json.tmp" "$tools_json" || true
local tools_tmp
tools_tmp=$(mktemp)
if [[ -f "$tools_json" ]]; then
jq '.vfio_iommu=true' "$tools_json" > "$tools_tmp" \
&& pmx_write_file "$tools_json" < "$tools_tmp" || true
else
printf '{}\n' > "$tools_tmp"
if jq '.vfio_iommu=true' "$tools_tmp" > "${tools_tmp}.next"; then
mv "${tools_tmp}.next" "$tools_tmp"
else
rm -f "${tools_tmp}.next"
fi
pmx_write_file "$tools_json" < "$tools_tmp" || true
fi
rm -f "$tools_tmp" "${tools_tmp}.next"
}
_enable_iommu_cmdline() {
pmx_journal_context "_enable_iommu_cmdline" "1.1" "switch_gpu_mode_direct.sh"
local cpu_vendor
cpu_vendor=$(grep -m1 "vendor_id" /proc/cpuinfo 2>/dev/null | awk '{print $3}')
@@ -870,14 +900,16 @@ _enable_iommu_cmdline() {
if [[ -f "$cmdline_file" ]] && grep -qE 'root=ZFS=|root=ZFS/' "$cmdline_file" 2>/dev/null; then
if ! grep -q "$iommu_param" "$cmdline_file"; then
cp "$cmdline_file" "${cmdline_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "s|\\s*$| ${iommu_param} iommu=pt|" "$cmdline_file"
pmx_edit_file "$cmdline_file" "s|\\s*$| ${iommu_param} iommu=pt|"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
fi
elif [[ -f "$grub_file" ]]; then
if ! grep -q "$iommu_param" "$grub_file"; then
cp "$grub_file" "${grub_file}.bak.$(date +%Y%m%d_%H%M%S)"
sed -i "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|" "$grub_file"
pmx_edit_file "$grub_file" "/GRUB_CMDLINE_LINUX_DEFAULT=/ s|\"$| ${iommu_param} iommu=pt\"|"
update-grub >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "update GRUB configuration" "update-grub"
fi
else
return 1
@@ -886,6 +918,7 @@ _enable_iommu_cmdline() {
}
switch_to_vm_mode() {
pmx_journal_context "switch_to_vm_mode" "1.1" "switch_gpu_mode_direct.sh"
detect_affected_lxc_for_selected
prompt_lxc_action_for_vm_mode
@@ -956,7 +989,9 @@ switch_to_vm_mode() {
if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then
msg_info "$(translate 'Updating initramfs (this may take a minute)...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1
pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture"
fi
}
@@ -975,6 +1010,7 @@ _type_has_remaining_vfio_ids() {
}
switch_to_lxc_mode() {
pmx_journal_context "switch_to_lxc_mode" "1.1" "switch_gpu_mode_direct.sh"
collect_selected_iommu_ids
detect_affected_vms_for_selected
prompt_vm_action_for_lxc_mode
@@ -1035,7 +1071,9 @@ switch_to_lxc_mode() {
if [[ "$HOST_CONFIG_CHANGED" == "true" ]]; then
msg_info "$(translate 'Updating initramfs (this may take a minute)...')"
update-initramfs -u -k all >>"$LOG_FILE" 2>&1
pmx_record_execution "rebuild initramfs" "update-initramfs -u -k all"
proxmox-boot-tool refresh >>"$LOG_FILE" 2>&1 || true
pmx_record_execution "refresh Proxmox boot configuration" "proxmox-boot-tool refresh"
msg_ok "$(translate 'initramfs updated')" | tee -a "$screen_capture"
fi
}
@@ -1154,6 +1192,7 @@ parse_arguments() {
# Main Entry Point
# ==========================================================
check_stale_vfio_config_switch_mode_direct() {
pmx_journal_context "check_stale_vfio_config_switch_mode_direct" "1.1" "switch_gpu_mode_direct.sh"
local vfio_conf="/etc/modprobe.d/vfio.conf"
[[ ! -f "$vfio_conf" ]] && return 0
@@ -1180,7 +1219,9 @@ check_stale_vfio_config_switch_mode_direct() {
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
pmx_record_execution "remove stale VFIO IDs" "_clean_vfio_conf_ids ${legacy_ids[*]}"
update-initramfs -u >/dev/null 2>&1 || true
pmx_record_execution "rebuild initramfs" "update-initramfs -u"
HOST_CONFIG_CHANGED=true
msg_ok "$(translate 'Stale VFIO entries removed and initramfs rebuilt.')" | tee -a "$screen_capture"
fi