From c88bea8220e26070266916b8c6eaad7de9b92d03 Mon Sep 17 00:00:00 2001 From: CanbiZ <47820557+MickLesk@users.noreply.github.com> Date: Thu, 27 Mar 2025 15:22:43 +0100 Subject: [PATCH] fixes --- scripts/tools/gpu-amd.func | 19 +++-- scripts/tools/gpu-intel.func | 37 +++++++-- scripts/tools/gpu-nvidia.func | 99 ++++++++++++------------ scripts/tools/hw-acceleration.sh | 125 ++++++++++--------------------- 4 files changed, 130 insertions(+), 150 deletions(-) diff --git a/scripts/tools/gpu-amd.func b/scripts/tools/gpu-amd.func index 412d0a0..08fed44 100644 --- a/scripts/tools/gpu-amd.func +++ b/scripts/tools/gpu-amd.func @@ -35,6 +35,8 @@ function passthrough_amd_to_lxc() { return 1 fi + grep -q "/dev/kfd" "$conf" 2>/dev/null && return 0 + { echo "# AMD ROCm GPU" echo "lxc.cgroup2.devices.allow: c 226:* rwm" @@ -49,13 +51,18 @@ function passthrough_amd_to_lxc() { function install_amd_tools_in_ct() { local ctid="$1" - msg info "Installing AMD GPU tools in CT $ctid..." - pct exec "$ctid" -- bash -c \ - "apt-get update && \ - apt-get install -y rocm-smi rocm-utils && \ - adduser \$(id -un 0) video && \ - adduser \$(id -un 0) render" >/dev/null 2>&1 || true + if pct exec "$ctid" -- grep -qi alpine /etc/os-release; then + msg warn "Skipping tool installation: Alpine container detected" + return 0 + fi + + msg info "Installing AMD GPU tools in CT $ctid..." + pct exec "$ctid" -- bash -c " + apt-get update && + DEBIAN_FRONTEND=noninteractive apt-get install -y rocm-smi rocm-utils && + adduser \$(id -un 0) video && + adduser \$(id -un 0) render" >/dev/null 2>&1 || true msg ok "Installed ROCm tools inside CT $ctid" } diff --git a/scripts/tools/gpu-intel.func b/scripts/tools/gpu-intel.func index 43e82e1..233ecb4 100644 --- a/scripts/tools/gpu-intel.func +++ b/scripts/tools/gpu-intel.func @@ -26,6 +26,11 @@ function intel_gpu_available() { [[ -e /dev/dri/renderD128 ]] && lspci | grep -qi 'VGA.*Intel' } +function is_alpine_ct() { + local ctid="$1" + pct exec "$ctid" -- sh -c 'grep -qi alpine /etc/os-release' >/dev/null 2>&1 +} + function passthrough_intel_to_lxc() { local ctid="$1" local conf="/etc/pve/lxc/${ctid}.conf" @@ -50,13 +55,33 @@ function passthrough_intel_to_lxc() { function install_intel_tools_in_ct() { local ctid="$1" + + if is_alpine_ct "$ctid"; then + msg warn "Skipping Intel tool install for Alpine CT $ctid" + return 0 + fi + + read -rp "Install non-free intel-media-va-driver (Debian only)? [y/N]: " confirm + if [[ "${confirm,,}" =~ ^(y|yes)$ ]]; then + msg info "Enabling non-free sources in $ctid..." + pct exec "$ctid" -- bash -c " +cat </etc/apt/sources.list.d/non-free.list +deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware +deb http://deb.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware +deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware +EOF + " + fi + msg info "Installing Intel tools in CT $ctid..." - pct exec "$ctid" -- bash -c \ - "apt-get update && \ - apt-get install -y va-driver-all vainfo intel-gpu-tools ocl-icd-libopencl1 intel-opencl-icd && \ - adduser \$(id -un 0) video && \ - adduser \$(id -un 0) render" >/dev/null 2>&1 + pct exec "$ctid" -- bash -c " + apt-get update -qq + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + va-driver-all vainfo intel-gpu-tools ocl-icd-libopencl1 intel-opencl-icd intel-media-va-driver-non-free >/dev/null 2>&1 + adduser root video >/dev/null 2>&1 || true + adduser root render >/dev/null 2>&1 || true + " - msg ok "Installed VAAPI & GPU tools inside CT $ctid" + msg ok "Installed Intel VAAPI tools in $ctid" } diff --git a/scripts/tools/gpu-nvidia.func b/scripts/tools/gpu-nvidia.func index 9d041b4..da22311 100644 --- a/scripts/tools/gpu-nvidia.func +++ b/scripts/tools/gpu-nvidia.func @@ -1,7 +1,6 @@ #!/usr/bin/env bash # NVIDIA GPU Integration for Proxmox LXC -# modular nvidia.func for LXC passthrough # Author: CanbiZ # License: MIT @@ -12,11 +11,19 @@ function nvidia_exit() { exit 0 } +function msg() { + local type="$1" + shift + case "$type" in + info) printf " \033[36m➤\033[0m %s\n" "$@" ;; + ok) printf " \033[32m✔\033[0m %s\n" "$@" ;; + warn) printf " \033[33m⚠\033[0m %s\n" "$@" >&2 ;; + err) printf " \033[31m✘\033[0m %s\n" "$@" >&2 ;; + esac +} + function nvidia_check_driver_installed() { - if ! command -v nvidia-smi &>/dev/null; then - return 1 - fi - return 0 + command -v nvidia-smi &>/dev/null } function nvidia_get_driver_version() { @@ -29,30 +36,30 @@ function nvidia_get_cuda_version() { function nvidia_validate_driver_version() { if ! nvidia_check_driver_installed; then - printf "✘ NVIDIA drivers not found on host\n" + msg err "NVIDIA drivers not found" nvidia_exit fi - local version - version=$(nvidia_get_driver_version) - local major=${version%%.*} + local ver major + ver=$(nvidia_get_driver_version) + major=${ver%%.*} if ((major < 500)); then - printf "⚠ Detected old NVIDIA driver version: %s\n" "$version" - read -r -p "Proceed anyway? [y/N] " confirm + msg warn "Detected old NVIDIA driver version: $ver" + read -rp "Continue anyway? [y/N]: " confirm [[ "${confirm,,}" =~ ^(y|yes)$ ]] || nvidia_exit fi } function nvidia_validate_cuda_version() { if ! nvidia_check_driver_installed; then - printf "✘ NVIDIA drivers not found on host\n" + msg err "NVIDIA drivers not found" nvidia_exit fi - local version - version=$(nvidia_get_cuda_version) - local major=${version%%.*} + local ver major + ver=$(nvidia_get_cuda_version) + major=${ver%%.*} if ((major < 11)); then - printf "⚠ Detected old CUDA version: %s\n" "$version" - read -r -p "Proceed anyway? [y/N] " confirm + msg warn "Detected old CUDA version: $ver" + read -rp "Continue anyway? [y/N]: " confirm [[ "${confirm,,}" =~ ^(y|yes)$ ]] || nvidia_exit fi } @@ -68,43 +75,34 @@ KERNEL=="nvidia", RUN+="/bin/bash -c '/usr/bin/nvidia-smi -L && chmod 666 /dev/n KERNEL=="nvidia_uvm", RUN+="/bin/bash -c '/usr/bin/nvidia-modprobe -c0 -u && chmod 0666 /dev/nvidia-uvm*'" EOF - printf "✔ NVIDIA kernel modules and udev rules applied\n" - printf "⚠ Please reboot the Proxmox host for changes to take effect\n" + msg ok "NVIDIA modules configured" + msg warn "Reboot the host to apply kernel changes" } function nvidia_select_gpu_minor() { - local menu=() - local maxwidth=0 - while IFS= read -r devdir; do - local pci=${devdir##*/} - local info="/proc/driver/nvidia/gpus/${pci}/information" + local menu=() max=0 + while IFS= read -r path; do + local dev="${path##*/}" + local info="/proc/driver/nvidia/gpus/${dev}/information" [[ -f "$info" ]] || continue local model minor model=$(awk -F': ' '/Model:/ {print $2}' "$info") minor=$(awk '/Device Minor/ {print $NF}' "$info") menu+=("$minor" "$model" "OFF") - ((${#model} > maxwidth)) && maxwidth=${#model} + (( ${#model} > max )) && max=${#model} done < <(find /proc/driver/nvidia/gpus -mindepth 1 -type d) - if ((${#menu[@]} == 0)); then - printf "✘ No NVIDIA GPU found\n" - return 1 - fi + [[ ${#menu[@]} -eq 0 ]] && msg err "No NVIDIA GPU found" && return 1 + [[ ${#menu[@]} -eq 3 ]] && printf "%s" "${menu[0]}" && return - if ((${#menu[@]} == 3)); then - printf "%s\n" "${menu[0]}" - else - whiptail --title "NVIDIA GPU Selection" --radiolist \ - "Select the GPU to passthrough:" 15 $((maxwidth + 40)) 6 \ - "${menu[@]}" 3>&1 1>&2 2>&3 - fi + whiptail --title "NVIDIA GPU Selection" --radiolist \ + "Select GPU for passthrough:" 15 $((max + 40)) 6 \ + "${menu[@]}" 3>&1 1>&2 2>&3 } function nvidia_lxc_passthrough() { - local container_id="$1" - local minor="$2" - local config="/etc/pve/lxc/${container_id}.conf" - + local ctid="$1" minor="$2" + local conf="/etc/pve/lxc/${ctid}.conf" local devices=( "/dev/nvidia${minor}" "/dev/nvidiactl" @@ -112,22 +110,19 @@ function nvidia_lxc_passthrough() { "/dev/nvidia-uvm-tools" ) - local devnum_list=() - + local devnums=() for dev in "${devices[@]}"; do [[ -e "$dev" ]] || continue - local major - major=$(stat -c '%t' "$dev") - local dec_major=$((16#$major)) - devnum_list+=("$dec_major") - echo "lxc.mount.entry: $dev ${dev##*/} none bind,optional,create=file" >>"$config" + local major_hex + major_hex=$(stat -c '%t' "$dev") + devnums+=($((16#$major_hex))) + echo "lxc.mount.entry: $dev ${dev##*/} none bind,optional,create=file" >>"$conf" done - echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$config" + echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >>"$conf" - for num in "${devnum_list[@]}"; do - echo "lxc.cgroup2.devices.allow: c ${num}:* rwm" >>"$config" + for n in "${devnums[@]}"; do + echo "lxc.cgroup2.devices.allow: c ${n}:* rwm" >>"$conf" done - printf "✔ NVIDIA passthrough configured for CT %s\n" "$container_id" -} + msg ok "NVIDIA passt diff --git a/scripts/tools/hw-acceleration.sh b/scripts/tools/hw-acceleration.sh index 712fd0a..886c514 100644 --- a/scripts/tools/hw-acceleration.sh +++ b/scripts/tools/hw-acceleration.sh @@ -1,39 +1,16 @@ #!/usr/bin/env bash -# + # Title: Proxmox LXC Hardware Passthrough & GPU Acceleration Setup -# Description: Enables hardware passthrough for USB, Intel, NVIDIA, AMD GPUs inside privileged LXC containers. -# Installs optional drivers/tools inside the container (vainfo, intel-gpu-tools, OpenCL, etc.) -# Only supports PRIVILEGED containers for GPU passthrough. -# License: MIT -# Author: MickLesk (CanbiZ) -# Repo: https://github.com/community-scripts/ProxmoxVED -# -# Usage: bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVED/raw/main/misc/hw-acceleration.sh)" -# -# Requires: -# - Proxmox VE 8.1+ -# - Privileged LXC Containers -# - GPU device available on host -# -# Features: -# - USB Serial Passthrough -# - Intel VAAPI passthrough + (optional) non-free drivers -# - NVIDIA GPU passthrough for LXC (binds /dev/nvidia*) -# - AMD GPU passthrough (experimental) -# - Container driver installation via APT -# - User group assignments (video/render) -# - Interactive menu system via whiptail -# -# Proxmox LXC Hardware Passthrough & GPU Acceleration Setup -# https://github.com/community-scripts/ProxmoxVED +# Maintainer: https://github.com/community-scripts/ProxmoxVED +# Includes: gpu-intel.func, gpu-nvidia.func, gpu-amd.func set -euo pipefail TEMP_DIR=$(mktemp -d) trap 'rm -rf $TEMP_DIR' EXIT -source <(wget -qO- https://github.com/community-scripts/ProxmoxVED/raw/main/scripts/tools/gpu-nvidia.func) source <(wget -qO- https://github.com/community-scripts/ProxmoxVED/raw/main/scripts/tools/gpu-intel.func) +source <(wget -qO- https://github.com/community-scripts/ProxmoxVED/raw/main/scripts/tools/gpu-nvidia.func) source <(wget -qO- https://github.com/community-scripts/ProxmoxVED/raw/main/scripts/tools/gpu-amd.func) function header_info() { @@ -62,31 +39,14 @@ function msg() { function prompt_features() { local features=() - printf "\nAvailable features:\n" - if [[ -e /dev/ttyUSB0 || -e /dev/ttyACM0 ]]; then - echo " [1] USB Passthrough" - features+=("usb") - fi - if [[ -e /dev/dri/renderD128 ]]; then - echo " [2] Intel iGPU (VAAPI)" - features+=("intel") - fi - if [[ -e /dev/nvidia0 ]]; then - echo " [3] NVIDIA GPU" - features+=("nvidia") - fi - if [[ -e /dev/kfd ]]; then - echo " [4] AMD GPU (ROCm)" - features+=("amd") - fi - - if [[ ${#features[@]} -eq 0 ]]; then - msg err "No supported hardware found on host." - exit 1 - fi - + printf "\nAvailable passthrough options:\n" + [[ -e /dev/ttyUSB0 || -e /dev/ttyACM0 ]] && echo " [1] USB" && features+=("usb") + [[ -e /dev/dri/renderD128 ]] && echo " [2] Intel VAAPI" && features+=("intel") + [[ -e /dev/nvidia0 ]] && echo " [3] NVIDIA GPU" && features+=("nvidia") + [[ -e /dev/kfd ]] && echo " [4] AMD GPU" && features+=("amd") + [[ ${#features[@]} -eq 0 ]] && msg err "No supported hardware detected." && exit 1 echo - read -rp "Enter number(s) separated by space (e.g. 1 3): " choices + read -rp "Select hardware (e.g. 1 3): " choices SELECTED_FEATURES=() for i in $choices; do case "$i" in @@ -96,20 +56,12 @@ function prompt_features() { 4) SELECTED_FEATURES+=("amd") ;; esac done - - if [[ ${#SELECTED_FEATURES[@]} -eq 0 ]]; then - msg warn "No valid feature selected." - exit 1 - fi + [[ ${#SELECTED_FEATURES[@]} -eq 0 ]] && msg warn "No valid feature selected." && exit 1 } function select_lxc_cts() { mapfile -t containers < <(pct list | awk 'NR>1 {print $1 "|" $2}') - if [[ ${#containers[@]} -eq 0 ]]; then - msg warn "No LXC containers found." - exit 1 - fi - + [[ ${#containers[@]} -eq 0 ]] && msg warn "No LXC containers found." && exit 1 echo echo "Available Containers:" for entry in "${containers[@]}"; do @@ -117,20 +69,21 @@ function select_lxc_cts() { name="${entry##*|}" echo " [$ctid] $name" done - echo read -rp "Enter container ID(s) separated by space: " SELECTED_CTIDS - if [[ -z "$SELECTED_CTIDS" ]]; then - msg warn "No containers selected." - exit 1 - fi + [[ -z "$SELECTED_CTIDS" ]] && msg warn "No containers selected." && exit 1 +} + +function is_alpine_container() { + local ctid="$1" + pct exec "$ctid" -- sh -c 'grep -qi alpine /etc/os-release' >/dev/null 2>&1 } function apply_usb_passthrough() { local conf="$1" grep -q "ttyUSB" "$conf" 2>/dev/null && return cat <>"$conf" -# USB Passthrough +# USB Serial Passthrough lxc.cgroup2.devices.allow: a lxc.cap.drop: lxc.cgroup2.devices.allow: c 188:* rwm @@ -157,45 +110,45 @@ function main() { for feature in "${SELECTED_FEATURES[@]}"; do case "$feature" in usb) - msg info "Applying USB passthrough to CT $ctid..." + msg info "Adding USB passthrough to CT $ctid..." apply_usb_passthrough "$conf" && updated=1 ;; intel) - msg info "Applying Intel VAAPI passthrough to CT $ctid..." + msg info "Intel passthrough setup for CT $ctid" passthrough_intel_to_lxc "$ctid" && install_intel_tools_in_ct "$ctid" && updated=1 ;; - amd) - msg info "Applying AMD GPU passthrough to CT $ctid..." - passthrough_amd_to_lxc "$ctid" && install_amd_tools_in_ct "$ctid" && updated=1 - ;; nvidia) - msg info "Checking NVIDIA GPU on host..." - check_nvidia_driver_status && check_cuda_version - gpu_minor=$(select_nvidia_gpu) || continue - passthrough_nvidia_to_lxc "$ctid" "$gpu_minor" && updated=1 + msg info "Validating NVIDIA setup..." + nvidia_validate_driver_version + nvidia_validate_cuda_version + local minor + minor=$(nvidia_select_gpu_minor) + nvidia_lxc_passthrough "$ctid" "$minor" && updated=1 + ;; + amd) + msg info "Applying AMD passthrough to CT $ctid..." + passthrough_amd_to_lxc "$ctid" && install_amd_tools_in_ct "$ctid" && updated=1 ;; esac done - if [[ "$updated" -eq 1 ]]; then - updated_cts+=("$ctid") - fi + [[ "$updated" -eq 1 ]] && updated_cts+=("$ctid") done echo if [[ ${#updated_cts[@]} -gt 0 ]]; then - msg ok "Updated: ${updated_cts[*]}" - read -rp "Restart updated container(s)? [y/N]: " restart - if [[ "${restart,,}" == "y" ]]; then + msg ok "Updated containers: ${updated_cts[*]}" + read -rp "Restart updated container(s)? [y/N]: " confirm + if [[ "${confirm,,}" == "y" ]]; then for ctid in "${updated_cts[@]}"; do pct reboot "$ctid" - msg ok "Restarted container $ctid" + msg ok "Restarted CT $ctid" done else - msg info "Manual restart required for: ${updated_cts[*]}" + msg info "Restart skipped." fi else - msg warn "No passthrough applied." + msg warn "No passthrough changes applied." fi }