
Some checks failed
Bump build.func Revision / bump-revision (push) Has been cancelled
Improves the vaapi_select_and_apply function to automatically select the VAAPI device when only one is detected, bypassing the whiptail selection dialog. This streamlines the user experience by reducing unnecessary prompts.
314 lines
8.8 KiB
Bash
314 lines
8.8 KiB
Bash
#!/usr/bin/env bash
|
||
# passthrough.func — host-side passthrough logic (VAAPI & NVIDIA) for LXC
|
||
# This file ONLY touches host config (/etc/pve/lxc/<CTID>.conf) and whiptail.
|
||
# Inside-CT package setup lives in *_inside_setup (called from build.func).
|
||
|
||
# --------------------------- Common helpers -----------------------------------
|
||
|
||
_whiptail_dims() {
|
||
local n="$1" L="$2"
|
||
local maxW=$((L + 8))
|
||
((maxW < 70)) && maxW=70
|
||
((maxW > 100)) && maxW=100
|
||
local H=$((10 + n * 2))
|
||
((H > 22)) && H=22
|
||
echo "$H $maxW"
|
||
}
|
||
|
||
select_hw_passthrough() {
|
||
local CTID="$1" CTTYPE="$2" APP="$3"
|
||
local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf"
|
||
|
||
if ! _is_gpu_app "$APP" && [[ "$CTTYPE" != "0" ]]; then
|
||
return
|
||
fi
|
||
|
||
local choices=()
|
||
[[ -d /dev/dri ]] && choices+=("VAAPI" "Intel/AMD GPU via VAAPI" OFF)
|
||
compgen -G "/dev/nvidia*" >/dev/null && choices+=("NVIDIA" "NVIDIA GPU passthrough" OFF)
|
||
|
||
# no GPUs found
|
||
[[ ${#choices[@]} -eq 0 ]] && {
|
||
msg_info "No GPU devices detected"
|
||
return
|
||
}
|
||
|
||
local SELECTED
|
||
if [[ ${#choices[@]} -eq 2 ]]; then
|
||
# both available → show whiptail
|
||
SELECTED=$(whiptail --title "GPU Passthrough" \
|
||
--checklist "Select GPU passthrough for CT $CTID:" 12 70 2 \
|
||
"${choices[@]}" 3>&1 1>&2 2>&3) || return
|
||
else
|
||
# only one option → auto-select
|
||
SELECTED="\"${choices[0]}\""
|
||
msg_info "Auto-selecting GPU passthrough: ${choices[0]}"
|
||
fi
|
||
|
||
for sel in $SELECTED; do
|
||
case "$sel" in
|
||
"\"VAAPI\"")
|
||
export ENABLE_VAAPI=1
|
||
vaapi_select_and_apply "$CTID" "$CTTYPE"
|
||
;;
|
||
"\"NVIDIA\"")
|
||
export ENABLE_NVIDIA=1
|
||
nvidia_passthrough_to_lxc "$CTID" "$CTTYPE"
|
||
;;
|
||
esac
|
||
done
|
||
}
|
||
|
||
# Apps that benefit from GPU passthrough (VAAPI + NVIDIA)
|
||
_GPU_APPS=(
|
||
immich
|
||
channels
|
||
emby
|
||
ersatztv
|
||
frigate
|
||
jellyfin
|
||
plex
|
||
scrypted
|
||
tdarr
|
||
unmanic
|
||
ollama
|
||
fileflows
|
||
"open webui"
|
||
tunarr
|
||
debian
|
||
)
|
||
|
||
_is_gpu_app() {
|
||
local app="$1"
|
||
local a
|
||
shopt -s nocasematch
|
||
for a in "${_GPU_APPS[@]}"; do
|
||
[[ "$app" == "$a" ]] && shopt -u nocasematch && return 0
|
||
done
|
||
shopt -u nocasematch
|
||
return 1
|
||
}
|
||
|
||
# ------------------------------ USB -------------------------------------------
|
||
|
||
usb_handle_passthrough() {
|
||
local CTID="$1" CTTYPE="$2"
|
||
local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf"
|
||
|
||
[[ "$CTTYPE" != "0" ]] && return 0 # USB passthrough only for privileged CTs
|
||
|
||
cat <<EOF >>"$LXC_CONFIG"
|
||
# USB passthrough
|
||
lxc.cgroup2.devices.allow: a
|
||
lxc.cap.drop:
|
||
lxc.cgroup2.devices.allow: c 188:* rwm
|
||
lxc.cgroup2.devices.allow: c 189:* rwm
|
||
lxc.mount.entry: /dev/serial/by-id dev/serial/by-id none bind,optional,create=dir
|
||
lxc.mount.entry: /dev/ttyUSB0 dev/ttyUSB0 none bind,optional,create=file
|
||
lxc.mount.entry: /dev/ttyUSB1 dev/ttyUSB1 none bind,optional,create=file
|
||
lxc.mount.entry: /dev/ttyACM0 dev/ttyACM0 none bind,optional,create=file
|
||
lxc.mount.entry: /dev/ttyACM1 dev/ttyACM1 none bind,optional,create=file
|
||
EOF
|
||
}
|
||
|
||
# ------------------------------ VAAPI -----------------------------------------
|
||
|
||
_vaapi_gid() {
|
||
local g="$1" gid
|
||
gid="$(getent group "$g" | cut -d: -f3)"
|
||
case "$g" in
|
||
video) echo "${gid:-44}" ;;
|
||
render) echo "${gid:-104}" ;;
|
||
*) echo "${gid:-44}" ;;
|
||
esac
|
||
}
|
||
|
||
_vaapi_pairs() {
|
||
local seen=() by real id idx card pci pci_info name
|
||
shopt -s nullglob
|
||
for by in /dev/dri/by-path/*-render /dev/dri/renderD*; do
|
||
[[ -e "$by" ]] || continue
|
||
real="$(readlink -f "$by" || true)"
|
||
[[ -e "$real" ]] || continue
|
||
id="$(basename "$real")"
|
||
[[ " ${seen[*]} " == *" $id "* ]] && continue
|
||
seen+=("$id")
|
||
|
||
idx="${id#renderD}"
|
||
[[ "$idx" =~ ^[0-9]+$ ]] && idx=$((idx - 128)) || idx=0
|
||
card="/dev/dri/card${idx}"
|
||
[[ -e "$card" ]] || card=""
|
||
|
||
if [[ "$by" == *"/by-path/"* ]]; then
|
||
pci="$(basename "$by" | sed -E 's/^pci-([0-9a-fA-F:.]+)-render/\1/')"
|
||
pci_info="$(lspci -nn 2>/dev/null | grep -i "${pci#0000:}" || true)"
|
||
name="${pci_info#*: }"
|
||
[[ -z "$name" ]] && name="GPU ${pci}"
|
||
else
|
||
name="DRM $(basename "$real")"
|
||
fi
|
||
|
||
label="$(basename "$real")"
|
||
[[ -n "$card" ]] && label+=" + $(basename "$card")"
|
||
label+=" – ${name}"
|
||
printf "%s:%s\t%s\n" "$real" "$card" "$label"
|
||
done
|
||
shopt -u nullglob
|
||
}
|
||
|
||
vaapi_select_and_apply() {
|
||
local CTID="$1" CTTYPE="$2"
|
||
local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf"
|
||
|
||
mapfile -t pairs < <(_vaapi_pairs)
|
||
((${#pairs[@]} == 0)) && {
|
||
msg_warn "No VAAPI devices detected – skipping."
|
||
return
|
||
}
|
||
|
||
# only one device -> auto-select
|
||
local SELECTED
|
||
if [[ ${#pairs[@]} -eq 1 ]]; then
|
||
SELECTED="${pairs[0]%%$'\t'*}"
|
||
msg_info "Auto-selecting VAAPI device: ${pairs[0]#*$'\t'}"
|
||
else
|
||
# more than one device -> show whiptail
|
||
local items=() maxlen=0
|
||
for p in "${pairs[@]}"; do
|
||
local devs="${p%%$'\t'*}" label="${p#*$'\t'}"
|
||
items+=("$devs" "$label" "OFF")
|
||
((${#label} > maxlen)) && maxlen=${#label}
|
||
done
|
||
read -r h w < <(_whiptail_dims $((${#items[@]} / 3)) "$maxlen")
|
||
|
||
SELECTED="$(whiptail --title "VAAPI Device Selection" \
|
||
--checklist "Select VAAPI devices for CT $CTID:" "$h" "$w" 6 \
|
||
"${items[@]}" 3>&1 1>&2 2>&3)" || {
|
||
msg_warn "VAAPI selection cancelled."
|
||
return
|
||
}
|
||
[[ -z "$SELECTED" ]] && {
|
||
msg_warn "No VAAPI devices selected."
|
||
return
|
||
}
|
||
fi
|
||
|
||
# Apply selection to LXC config
|
||
local DID_MOUNT_DRI=0 idx=0
|
||
for dev in $SELECTED; do
|
||
dev="${dev%\"}"
|
||
dev="${dev#\"}"
|
||
IFS=":" read -r path card <<<"$dev"
|
||
for d in "$path" "$card"; do
|
||
[[ -n "$d" && -e "$d" ]] || continue
|
||
if [[ "$CTTYPE" == "0" ]]; then
|
||
[[ $DID_MOUNT_DRI -eq 0 && -d /dev/dri ]] && {
|
||
echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG"
|
||
DID_MOUNT_DRI=1
|
||
}
|
||
if mm=$(stat -c '%t:%T' "$d" | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}'); then
|
||
echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG"
|
||
echo "lxc.mount.entry: $d $d none bind,optional,create=file" >>"$LXC_CONFIG"
|
||
fi
|
||
else
|
||
gid=$([[ "$d" =~ renderD ]] && _vaapi_gid render || _vaapi_gid video)
|
||
echo "dev${idx}: $d,gid=${gid}" >>"$LXC_CONFIG"
|
||
idx=$((idx + 1))
|
||
fi
|
||
done
|
||
done
|
||
|
||
# Fallback only for privileged CTs
|
||
[[ "$CTTYPE" == "0" ]] && cat <<'EOF' >>"$LXC_CONFIG"
|
||
# VAAPI fallback
|
||
lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir
|
||
lxc.cgroup2.devices.allow: c 226:* rwm
|
||
EOF
|
||
}
|
||
|
||
# ----------------------------- NVIDIA -----------------------------------------
|
||
|
||
nvidia_passthrough_to_lxc() {
|
||
local CTID="$1" CTTYPE="$2"
|
||
local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf"
|
||
local found=0
|
||
|
||
for dev in /dev/nvidia*; do
|
||
[[ -e "$dev" ]] || continue
|
||
found=1
|
||
if mm="$(stat -c '%t:%T' "$dev" | awk -F: '{printf "%d:%d","0x"$1,"0x"$2}')"; then
|
||
echo "lxc.cgroup2.devices.allow: c $mm rwm" >>"$LXC_CONFIG"
|
||
echo "lxc.mount.entry: $dev $dev none bind,optional,create=file" >>"$LXC_CONFIG"
|
||
fi
|
||
done
|
||
((found == 0)) && {
|
||
msg_warn "No NVIDIA devices found."
|
||
return
|
||
}
|
||
|
||
[[ -d /dev/dri && "$CTTYPE" == "0" ]] && echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG"
|
||
msg_ok "NVIDIA devices mapped to CT ${CTID}"
|
||
}
|
||
|
||
install_vaapi_userland_interactive() {
|
||
. /etc/os-release
|
||
if [[ "$VERSION_CODENAME" == "trixie" ]]; then
|
||
read -r -p "${TAB3}Do you need the intel-media-va-driver-non-free driver for HW encoding (Debian 13 only)? <y/N> " prompt
|
||
if [[ ${prompt,,} =~ ^(y|yes)$ ]]; then
|
||
msg_info "Installing Intel Hardware Acceleration (non-free)"
|
||
cat <<'EOF' >/etc/apt/sources.list.d/non-free.sources
|
||
Types: deb deb-src
|
||
URIs: http://deb.debian.org/debian
|
||
Suites: trixie
|
||
Components: non-free non-free-firmware
|
||
|
||
Types: deb deb-src
|
||
URIs: http://deb.debian.org/debian-security
|
||
Suites: trixie-security
|
||
Components: non-free non-free-firmware
|
||
|
||
Types: deb deb-src
|
||
URIs: http://deb.debian.org/debian
|
||
Suites: trixie-updates
|
||
Components: non-free non-free-firmware
|
||
EOF
|
||
$STD apt-get update
|
||
$STD apt-get install -y \
|
||
intel-media-va-driver-non-free \
|
||
ocl-icd-libopencl1 \
|
||
mesa-opencl-icd \
|
||
mesa-va-drivers \
|
||
libvpl2 \
|
||
vainfo \
|
||
intel-gpu-tools
|
||
msg_ok "Installed Intel Hardware Acceleration (non-free)"
|
||
return
|
||
fi
|
||
fi
|
||
|
||
msg_info "Installing Intel Hardware Acceleration (open packages)"
|
||
$STD apt-get update
|
||
$STD apt-get install -y \
|
||
va-driver-all \
|
||
ocl-icd-libopencl1 \
|
||
mesa-opencl-icd \
|
||
mesa-va-drivers \
|
||
vainfo \
|
||
intel-gpu-tools
|
||
msg_ok "Installed Intel Hardware Acceleration (open packages)"
|
||
}
|
||
|
||
install_nvidia_userland_interactive() {
|
||
msg_info "Installing NVIDIA Userland"
|
||
$STD apt-get update
|
||
$STD apt-get install -y \
|
||
nvidia-driver \
|
||
nvidia-utils \
|
||
libnvidia-encode1 \
|
||
libcuda1 || {
|
||
msg_error "Failed to install NVIDIA packages"
|
||
return 1
|
||
}
|
||
msg_ok "Installed NVIDIA Userland"
|
||
}
|