
Some checks failed
Bump build.func Revision / bump-revision (push) Has been cancelled
Added direct installation of VAAPI and NVIDIA drivers inside LXC containers during setup, removing reliance on custom in-container scripts. The process now ensures required packages are installed and user permissions are set, improving automation and compatibility for supported distributions.
392 lines
12 KiB
Bash
392 lines
12 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 tools.func (hwaccel_setup_in_ct / nvidia_setup_in_ct).
|
||
|
||
# --------------------------- 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"
|
||
}
|
||
|
||
# ------------------------------ 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)"
|
||
if [[ -z "$gid" ]]; then
|
||
case "$g" in
|
||
video) echo 44 ;;
|
||
render) echo 104 ;;
|
||
*) echo 44 ;;
|
||
esac
|
||
else
|
||
echo "$gid"
|
||
fi
|
||
}
|
||
|
||
_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}"
|
||
if [[ "$idx" =~ ^[0-9]+$ ]]; then
|
||
idx=$((idx - 128))
|
||
else
|
||
idx=0
|
||
fi
|
||
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"
|
||
|
||
local pairs=() items=() maxlen=0 n h w
|
||
mapfile -t pairs < <(_vaapi_pairs)
|
||
|
||
if ((${#pairs[@]} == 0)); then
|
||
msg_warn "No VAAPI devices detected – skipping."
|
||
return 0
|
||
fi
|
||
|
||
for p in "${pairs[@]}"; do
|
||
local devs="${p%%$'\t'*}"
|
||
local label="${p#*$'\t'}"
|
||
items+=("$devs" "$label" "OFF")
|
||
((${#label} > maxlen)) && maxlen=${#label}
|
||
done
|
||
n=$((${#items[@]} / 3))
|
||
read -r h w < <(_whiptail_dims "$n" "$maxlen")
|
||
|
||
if [[ "$CTTYPE" == "0" ]]; then
|
||
whiptail --title "VAAPI passthrough" --msgbox "\
|
||
VAAPI passthrough will be enabled.
|
||
|
||
• Privileged CT: full DRM access
|
||
• You may need to install drivers inside the CT (intel-media-driver, vainfo)." 12 "$w"
|
||
else
|
||
whiptail --title "VAAPI passthrough (unprivileged)" --msgbox "\
|
||
Unprivileged CT: VAAPI may be limited.
|
||
|
||
If it fails, consider a privileged CT. You may still need drivers inside the CT." 12 "$w"
|
||
fi
|
||
|
||
local SELECTED
|
||
SELECTED="$(
|
||
whiptail --title "VAAPI Device Selection" \
|
||
--checklist "Select GPU / VAAPI device(s) to passthrough:" "$h" "$w" "$((n > 6 ? 6 : n))" \
|
||
"${items[@]}" 3>&1 1>&2 2>&3
|
||
)" || {
|
||
msg_warn "VAAPI selection cancelled."
|
||
return 0
|
||
}
|
||
|
||
[[ -z "$SELECTED" ]] && {
|
||
msg_warn "No devices selected – skipping."
|
||
return 0
|
||
}
|
||
|
||
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
|
||
if [[ "$DID_MOUNT_DRI" -eq 0 && -d /dev/dri ]]; then
|
||
echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG"
|
||
DID_MOUNT_DRI=1
|
||
fi
|
||
if mm=$(stat -c '%t:%T' "$d" 2>/dev/null | 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"
|
||
else
|
||
msg_warn "Could not stat $d – skipping."
|
||
fi
|
||
else
|
||
local gid
|
||
if [[ "$d" =~ renderD ]]; then gid="$(_vaapi_gid render)"; else gid="$(_vaapi_gid video)"; fi
|
||
echo "dev${idx}: $d,gid=${gid}" >>"$LXC_CONFIG"
|
||
idx=$((idx + 1))
|
||
fi
|
||
done
|
||
done
|
||
|
||
# fallback for card0/card1 flip
|
||
if [[ "$CTTYPE" == "0" ]]; then
|
||
cat <<'EOF' >>"$LXC_CONFIG"
|
||
# VAAPI fallback: bind /dev/dri and allow 226:* to survive node flips
|
||
lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir
|
||
lxc.cgroup2.devices.allow: c 226:* rwm
|
||
EOF
|
||
fi
|
||
}
|
||
|
||
vaapi_handle_passthrough() {
|
||
local CTID="$1" CTTYPE="$2" APP="$3"
|
||
|
||
local VAAPI_APPS=(immich Channels Emby ErsatzTV Frigate Jellyfin Plex Scrypted Tdarr Unmanic Ollama FileFlows "Open WebUI" Tunarr Debian)
|
||
local is_vaapi_app=false
|
||
for a in "${VAAPI_APPS[@]}"; do [[ "$APP" == "$a" ]] && is_vaapi_app=true && break; done
|
||
|
||
if [[ "$CTTYPE" == "0" || "$is_vaapi_app" == "true" ]]; then
|
||
vaapi_select_and_apply "$CTID" "$CTTYPE"
|
||
|
||
# ensure CT is running
|
||
if ! pct status "$CTID" | grep -q running; then
|
||
pct start "$CTID"
|
||
for i in {1..10}; do
|
||
pct status "$CTID" | grep -q running && break
|
||
sleep 1
|
||
done
|
||
fi
|
||
|
||
# run apt install directly inside CT
|
||
pct exec "$CTID" -- bash -lc '
|
||
. /etc/os-release
|
||
case "$VERSION_CODENAME" in
|
||
trixie|noble)
|
||
apt-get update
|
||
apt-get install -y intel-media-va-driver-non-free ocl-icd-libopencl1 \
|
||
mesa-opencl-icd mesa-va-drivers libvpl2 vainfo intel-gpu-tools
|
||
;;
|
||
*)
|
||
apt-get update
|
||
apt-get install -y va-driver-all ocl-icd-libopencl1 \
|
||
mesa-opencl-icd mesa-va-drivers vainfo intel-gpu-tools
|
||
;;
|
||
esac
|
||
if [[ "'"$CTTYPE"'" == "0" ]]; then
|
||
adduser "$(id -un)" video || true
|
||
adduser "$(id -un)" render || true
|
||
fi
|
||
'
|
||
fi
|
||
}
|
||
|
||
# ----------------------------- NVIDIA -----------------------------------------
|
||
|
||
nvidia_passthrough_to_lxc() {
|
||
local CTID="$1" CTTYPE="${2:-0}"
|
||
local LXC_CONFIG="/etc/pve/lxc/${CTID}.conf"
|
||
local found=0 dev mm
|
||
|
||
for dev in /dev/nvidia0 /dev/nvidia1 /dev/nvidiactl /dev/nvidia-uvm /dev/nvidia-uvm-tools /dev/nvidia-modeset; do
|
||
[[ -e "$dev" ]] || continue
|
||
found=1
|
||
if mm="$(stat -c '%t:%T' "$dev" 2>/dev/null | 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
|
||
|
||
if [[ "$found" -eq 0 ]]; then
|
||
msg_warn "No /dev/nvidia* devices found on host; skipping NVIDIA passthrough."
|
||
return 0
|
||
fi
|
||
|
||
# optional: expose /dev/dri for apps probing VAAPI; harmless with NVIDIA
|
||
if [[ -d /dev/dri && "$CTTYPE" == "0" ]]; then
|
||
echo "lxc.mount.entry: /dev/dri /dev/dri none bind,optional,create=dir" >>"$LXC_CONFIG"
|
||
fi
|
||
|
||
msg_ok "NVIDIA devices mapped to CT ${CTID}"
|
||
}
|
||
|
||
nvidia_handle_passthrough() {
|
||
local CTID="$1" CTTYPE="$2" APP="$3"
|
||
|
||
compgen -G "/dev/nvidia*" >/dev/null || return 0
|
||
|
||
if whiptail --title "NVIDIA passthrough" \
|
||
--yesno "NVIDIA GPU detected. Map /dev/nvidia* into CT $CTID and install drivers inside?" 12 70; then
|
||
|
||
nvidia_passthrough_to_lxc "$CTID" "$CTTYPE"
|
||
|
||
if ! pct status "$CTID" | grep -q running; then
|
||
pct start "$CTID"
|
||
for i in {1..10}; do
|
||
pct status "$CTID" | grep -q running && break
|
||
sleep 1
|
||
done
|
||
fi
|
||
|
||
pct exec "$CTID" -- bash -lc '
|
||
. /etc/os-release
|
||
apt-get update
|
||
apt-get install -y nvidia-driver nvidia-utils libnvidia-encode1 libcuda1
|
||
if [[ "'"$CTTYPE"'" == "0" ]]; then
|
||
adduser "$(id -un)" video || true
|
||
fi
|
||
'
|
||
fi
|
||
}
|
||
|
||
# ------------------------------------------------------------------------------
|
||
# Hardware acceleration setup inside container (Intel/AMD via VAAPI)
|
||
# Debian 12 (bookworm), Debian 13 (trixie), Ubuntu 24.04 (noble)
|
||
# Usage: hwaccel_setup_in_ct <CTTYPE 0|1> [--nonfree-intel]
|
||
# ------------------------------------------------------------------------------
|
||
hwaccel_setup_in_ct() {
|
||
local CTTYPE="$1" NONFREE=0
|
||
[[ "$2" == "--nonfree-intel" ]] && NONFREE=1
|
||
|
||
local ID VERSION_CODENAME
|
||
if [[ -r /etc/os-release ]]; then . /etc/os-release; fi
|
||
ID="${ID:-debian}"
|
||
VERSION_CODENAME="${VERSION_CODENAME:-bookworm}"
|
||
|
||
msg_info "Setting up VAAPI userland for ${ID^} ($VERSION_CODENAME)"
|
||
|
||
case "$ID" in
|
||
debian | ubuntu)
|
||
if ((NONFREE)) && [[ "$VERSION_CODENAME" =~ (trixie|noble) ]]; then
|
||
# Debian 13 / Ubuntu 24.04 — enable non-free Intel media (Debian deb822)
|
||
if [[ "$VERSION_CODENAME" == "trixie" ]]; then
|
||
cat >/etc/apt/sources.list.d/non-free.sources <<'SRC'
|
||
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
|
||
SRC
|
||
fi
|
||
|
||
$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
|
||
else
|
||
$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
|
||
fi
|
||
;;
|
||
*)
|
||
msg_warn "Unsupported distro ($ID $VERSION_CODENAME) – skipping VAAPI setup."
|
||
return 0
|
||
;;
|
||
esac
|
||
|
||
if [[ "$CTTYPE" == "0" ]]; then
|
||
$STD adduser "$(id -un)" video || true
|
||
$STD adduser "$(id -un)" render || true
|
||
fi
|
||
|
||
msg_ok "VAAPI userland ready"
|
||
}
|
||
|
||
# ------------------------------------------------------------------------------
|
||
# NVIDIA userland inside container
|
||
# Debian 12/13, Ubuntu 24.04
|
||
# Usage: nvidia_setup_in_ct <CTTYPE 0|1>
|
||
# ------------------------------------------------------------------------------
|
||
nvidia_setup_in_ct() {
|
||
local CTTYPE="$1"
|
||
|
||
local ID VERSION_CODENAME
|
||
if [[ -r /etc/os-release ]]; then . /etc/os-release; fi
|
||
ID="${ID:-debian}"
|
||
VERSION_CODENAME="${VERSION_CODENAME:-bookworm}"
|
||
|
||
msg_info "Installing NVIDIA userland on ${ID^} ($VERSION_CODENAME)"
|
||
|
||
case "$ID" in
|
||
debian | ubuntu)
|
||
$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_warn "Unsupported distro ($ID $VERSION_CODENAME) – skipping NVIDIA setup."
|
||
return 0
|
||
;;
|
||
esac
|
||
|
||
if [[ "$CTTYPE" == "0" ]]; then
|
||
$STD adduser "$(id -un)" video || true
|
||
fi
|
||
|
||
msg_ok "NVIDIA userland ready"
|
||
}
|