mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-04-25 08:56:21 +00:00
update nstall_coral_pve9.sh
This commit is contained in:
@@ -1,204 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ==========================================================
|
||||
# ProxMenux - A menu-driven script for Proxmox VE management
|
||||
# ==========================================================
|
||||
# Author : MacRimi
|
||||
# Copyright : (c) 2024 MacRimi
|
||||
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
|
||||
# Version : 1.1
|
||||
# Last Updated: 17/08/2025
|
||||
# ==========================================================
|
||||
# Description:
|
||||
# This script automates the process of enabling and configuring Intel Integrated GPU (iGPU) support in Proxmox VE LXC containers.
|
||||
# Its goal is to simplify the configuration of hardware-accelerated graphical capabilities within containers, allowing for efficient
|
||||
# use of Intel iGPUs for tasks such as transcoding, rendering, and accelerating graphics-intensive applications.
|
||||
# ==========================================================
|
||||
|
||||
# Configuration ============================================
|
||||
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
|
||||
BASE_DIR="/usr/local/share/proxmenux"
|
||||
UTILS_FILE="$BASE_DIR/utils.sh"
|
||||
VENV_PATH="/opt/googletrans-env"
|
||||
|
||||
if [[ -f "$UTILS_FILE" ]]; then
|
||||
source "$UTILS_FILE"
|
||||
fi
|
||||
|
||||
load_language
|
||||
initialize_cache
|
||||
|
||||
# ==========================================================
|
||||
|
||||
|
||||
|
||||
|
||||
select_container() {
|
||||
|
||||
CONTAINERS=$(pct list | awk 'NR>1 {print $1, $3}' | xargs -n2)
|
||||
if [ -z "$CONTAINERS" ]; then
|
||||
msg_error "$(translate 'No containers available in Proxmox.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONTAINER_ID=$(whiptail --title "$(translate 'Select Container')" \
|
||||
--menu "$(translate 'Select the LXC container:')" 20 70 10 $CONTAINERS 3>&1 1>&2 2>&3)
|
||||
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
msg_error "$(translate 'No container selected. Exiting.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! pct list | awk 'NR>1 {print $1}' | grep -qw "$CONTAINER_ID"; then
|
||||
msg_error "$(translate 'Container with ID') $CONTAINER_ID $(translate 'does not exist. Exiting.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Container selected:') $CONTAINER_ID"
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
validate_container_id() {
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
msg_error "$(translate 'Container ID not defined. Make sure to select a container first.')"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
if pct status "$CONTAINER_ID" | grep -q "running"; then
|
||||
msg_info "$(translate 'Stopping the container before applying configuration...')"
|
||||
pct stop "$CONTAINER_ID"
|
||||
msg_ok "$(translate 'Container stopped.')"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
|
||||
configure_lxc_for_igpu() {
|
||||
validate_container_id
|
||||
|
||||
CONFIG_FILE="/etc/pve/lxc/${CONTAINER_ID}.conf"
|
||||
[[ -f "$CONFIG_FILE" ]] || { msg_error "$(translate 'Configuration file for container') $CONTAINER_ID $(translate 'not found.')"; exit 1; }
|
||||
|
||||
|
||||
if [[ ! -d /dev/dri ]]; then
|
||||
modprobe i915 2>/dev/null || true
|
||||
for _ in {1..5}; do
|
||||
[[ -d /dev/dri ]] && break
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
CT_TYPE=$(pct config "$CONTAINER_ID" | awk '/^unprivileged:/ {print $2}')
|
||||
[[ -z "$CT_TYPE" ]] && CT_TYPE="0"
|
||||
|
||||
msg_info "$(translate 'Configuring Intel iGPU passthrough for container...')"
|
||||
|
||||
for rn in /dev/dri/renderD*; do
|
||||
[[ -e "$rn" ]] || continue
|
||||
chmod 660 "$rn" 2>/dev/null || true
|
||||
chgrp render "$rn" 2>/dev/null || true
|
||||
done
|
||||
|
||||
mapfile -t RENDER_NODES < <(find /dev/dri -maxdepth 1 -type c -name 'renderD*' 2>/dev/null || true)
|
||||
mapfile -t CARD_NODES < <(find /dev/dri -maxdepth 1 -type c -name 'card*' 2>/dev/null || true)
|
||||
FB_NODE=""
|
||||
[[ -e /dev/fb0 ]] && FB_NODE="/dev/fb0"
|
||||
|
||||
if [[ ${#RENDER_NODES[@]} -eq 0 && ${#CARD_NODES[@]} -eq 0 && -z "$FB_NODE" ]]; then
|
||||
msg_warn "$(translate 'No VA-API devices found on host (/dev/dri*, /dev/fb0). Is i915 loaded?')"
|
||||
return 0
|
||||
fi
|
||||
|
||||
if grep -q '^features:' "$CONFIG_FILE"; then
|
||||
grep -Eq '^features:.*(^|,)\s*nesting=1(\s|,|$)' "$CONFIG_FILE" || sed -i 's/^features:\s*/&nesting=1, /' "$CONFIG_FILE"
|
||||
else
|
||||
echo "features: nesting=1" >> "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
if [[ "$CT_TYPE" == "0" ]]; then
|
||||
|
||||
sed -i '/^lxc\.cgroup2\.devices\.allow:\s*c\s*226:/d' "$CONFIG_FILE"
|
||||
sed -i '\|^lxc\.mount\.entry:\s*/dev/dri|d' "$CONFIG_FILE"
|
||||
sed -i '\|^lxc\.mount\.entry:\s*/dev/fb0|d' "$CONFIG_FILE"
|
||||
|
||||
echo "lxc.cgroup2.devices.allow: c 226:* rwm" >> "$CONFIG_FILE"
|
||||
echo "lxc.mount.entry: /dev/dri dev/dri none bind,optional,create=dir" >> "$CONFIG_FILE"
|
||||
[[ -n "$FB_NODE" ]] && echo "lxc.mount.entry: /dev/fb0 dev/fb0 none bind,optional,create=file" >> "$CONFIG_FILE"
|
||||
|
||||
|
||||
else
|
||||
sed -i '/^dev[0-9]\+:/d' "$CONFIG_FILE"
|
||||
|
||||
idx=0
|
||||
for c in "${CARD_NODES[@]}"; do
|
||||
echo "dev${idx}: $c,gid=44" >> "$CONFIG_FILE"
|
||||
idx=$((idx+1))
|
||||
done
|
||||
for r in "${RENDER_NODES[@]}"; do
|
||||
echo "dev${idx}: $r,gid=104" >> "$CONFIG_FILE"
|
||||
idx=$((idx+1))
|
||||
done
|
||||
|
||||
fi
|
||||
msg_ok "$(translate 'iGPU configuration added to container') $CONTAINER_ID."
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
install_igpu_in_container() {
|
||||
|
||||
msg_info2 "$(translate 'Installing iGPU drivers inside the container...')"
|
||||
tput sc
|
||||
LOG_FILE=$(mktemp)
|
||||
|
||||
|
||||
pct start "$CONTAINER_ID" >/dev/null 2>&1
|
||||
|
||||
script -q -c "pct exec \"$CONTAINER_ID\" -- bash -c '
|
||||
set -e
|
||||
getent group video >/dev/null || groupadd -g 44 video
|
||||
getent group render >/dev/null || groupadd -g 104 render
|
||||
usermod -aG video,render root || true
|
||||
|
||||
apt-get update >/dev/null 2>&1
|
||||
apt-get install -y va-driver-all ocl-icd-libopencl1 intel-opencl-icd vainfo intel-gpu-tools
|
||||
|
||||
chgrp video /dev/dri 2>/dev/null || true
|
||||
chmod 755 /dev/dri 2>/dev/null || true
|
||||
'" "$LOG_FILE"
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
tput rc
|
||||
tput ed
|
||||
rm -f "$LOG_FILE"
|
||||
msg_ok "$(translate 'iGPU drivers installed inside the container.')"
|
||||
else
|
||||
tput rc
|
||||
tput ed
|
||||
msg_error "$(translate 'Failed to install iGPU drivers inside the container.')"
|
||||
cat "$LOG_FILE"
|
||||
rm -f "$LOG_FILE"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
select_container
|
||||
show_proxmenux_logo
|
||||
msg_title "$(translate "Add HW iGPU acceleration to an LXC")"
|
||||
configure_lxc_for_igpu
|
||||
install_igpu_in_container
|
||||
|
||||
|
||||
msg_success "$(translate 'iGPU configuration completed in container') $CONTAINER_ID."
|
||||
echo -e
|
||||
msg_success "$(translate "Press Enter to return to menu...")"
|
||||
read -r
|
||||
@@ -3,8 +3,8 @@
|
||||
# =========================================
|
||||
# Author : MacRimi
|
||||
# License : MIT
|
||||
# Version : 1.4 (kernel-conditional patches, direct DKMS, no debuild)
|
||||
# Last Updated: 01/04/2026
|
||||
# Version : 1.5 (feranick fork primary; kernel 6.12+ support; broken-pkg recovery)
|
||||
# Last Updated: 17/04/2026
|
||||
# =========================================
|
||||
|
||||
LOCAL_SCRIPTS="/usr/local/share/proxmenux/scripts"
|
||||
@@ -72,6 +72,119 @@ pre_install_prompt() {
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Clean up a broken gasket-dkms package state.
|
||||
#
|
||||
# Context: if the user had gasket-dkms installed as a .deb (typical after
|
||||
# following the Coral docs or via libedgetpu1-std's dependency chain), a
|
||||
# kernel upgrade on PVE 9 triggers dkms autoinstall → compile fails on
|
||||
# kernel 6.12+ → dpkg leaves the package half-configured. That broken state
|
||||
# blocks further apt operations (including our own `apt-get install` below)
|
||||
# with "E: Sub-process /usr/bin/dpkg returned an error code (1)".
|
||||
# ============================================================
|
||||
cleanup_broken_gasket_dkms() {
|
||||
# dpkg status codes of interest (first column of `dpkg -l`):
|
||||
# iF half-configured
|
||||
# iU unpacked (not configured)
|
||||
# iH half-installed
|
||||
# Also catch the case where the package is installed but the DKMS source tree
|
||||
# is broken (kernel upgrade without rebuild).
|
||||
local pkg_state
|
||||
pkg_state=$(dpkg -l gasket-dkms 2>/dev/null | awk '/^[a-zA-Z][a-zA-Z]/ {print $1}' | tail -1)
|
||||
|
||||
if [[ -z "$pkg_state" ]]; then
|
||||
return 0 # package not present, nothing to clean
|
||||
fi
|
||||
|
||||
# Any state other than "ii" (installed+configured cleanly) or "rc"
|
||||
# (removed but config remaining) warrants proactive cleanup.
|
||||
case "$pkg_state" in
|
||||
ii|rc)
|
||||
# Even when state is "ii", a stale DKMS module may exist — drop it to
|
||||
# ensure our fresh build replaces the old one.
|
||||
msg_info "$(translate 'Removing any pre-existing gasket-dkms package...')"
|
||||
dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1 || true
|
||||
dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true
|
||||
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
|
||||
dpkg --purge --force-all gasket-dkms >>"$LOG_FILE" 2>&1 || true
|
||||
dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true
|
||||
# apt-get install -f resolves any remaining dependency issues left by
|
||||
# the forced removal above.
|
||||
apt-get install -f -y >>"$LOG_FILE" 2>&1 || true
|
||||
msg_ok "$(translate 'Broken gasket-dkms package state recovered.')"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Clone the gasket driver sources.
|
||||
#
|
||||
# Primary: feranick/gasket-driver — community fork, actively maintained,
|
||||
# already carries patches for kernel
|
||||
# 6.10 / 6.12 / 6.13. Preferred.
|
||||
# Fallback: google/gasket-driver — upstream, stale. Requires the manual
|
||||
# compatibility patches applied below.
|
||||
#
|
||||
# Sets GASKET_SOURCE_USED to "feranick" or "google" so downstream steps know
|
||||
# whether to apply the local patches.
|
||||
# ============================================================
|
||||
clone_gasket_sources() {
|
||||
local FERANICK_URL="https://github.com/feranick/gasket-driver.git"
|
||||
local GOOGLE_URL="https://github.com/google/gasket-driver.git"
|
||||
|
||||
cd /tmp || exit 1
|
||||
rm -rf gasket-driver >>"$LOG_FILE" 2>&1
|
||||
|
||||
msg_info "$(translate 'Cloning Coral driver repository (feranick fork)...')"
|
||||
if git clone --depth=1 "$FERANICK_URL" gasket-driver >>"$LOG_FILE" 2>&1; then
|
||||
GASKET_SOURCE_USED="feranick"
|
||||
msg_ok "$(translate 'feranick/gasket-driver cloned (actively maintained, kernel 6.12+ ready).')"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_warn "$(translate 'feranick fork unreachable. Falling back to google/gasket-driver...')"
|
||||
rm -rf gasket-driver >>"$LOG_FILE" 2>&1
|
||||
if git clone --depth=1 "$GOOGLE_URL" gasket-driver >>"$LOG_FILE" 2>&1; then
|
||||
GASKET_SOURCE_USED="google"
|
||||
msg_ok "$(translate 'google/gasket-driver cloned (fallback — will apply local patches).')"
|
||||
return 0
|
||||
fi
|
||||
|
||||
msg_error "$(translate 'Could not clone any gasket-driver repository. Check your internet connection and /tmp/coral_install.log')"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
# ============================================================
|
||||
# On a failed DKMS build, surface the most relevant lines of make.log
|
||||
# on-screen so the user (and bug reports) have immediate context without
|
||||
# having to open the log file manually.
|
||||
# ============================================================
|
||||
show_dkms_build_failure() {
|
||||
local make_log="/var/lib/dkms/gasket/1.0/build/make.log"
|
||||
echo "" >&2
|
||||
msg_warn "$(translate 'DKMS build failed. Last lines of make.log:')"
|
||||
if [[ -f "$make_log" ]]; then
|
||||
# Also append the full log to our install log for post-mortem.
|
||||
{
|
||||
echo "---- /var/lib/dkms/gasket/1.0/build/make.log ----"
|
||||
cat "$make_log"
|
||||
} >>"$LOG_FILE" 2>&1
|
||||
tail -n 50 "$make_log" >&2
|
||||
else
|
||||
echo "$(translate '(make.log not found — DKMS may have failed before invoking make)')" >&2
|
||||
fi
|
||||
echo "" >&2
|
||||
echo -e "${TAB}${BL}$(translate 'Full log:')${CL} /tmp/coral_install.log" >&2
|
||||
echo "" >&2
|
||||
}
|
||||
|
||||
install_coral_host() {
|
||||
show_proxmenux_logo
|
||||
: >"$LOG_FILE"
|
||||
@@ -82,6 +195,9 @@ install_coral_host() {
|
||||
KMAJ=$(echo "$KVER" | cut -d. -f1)
|
||||
KMIN=$(echo "$KVER" | cut -d. -f2 | cut -d+ -f1 | cut -d- -f1)
|
||||
|
||||
# Recover from a broken gasket-dkms package state (typical after a kernel
|
||||
# upgrade on PVE 9) before attempting any apt operations.
|
||||
cleanup_broken_gasket_dkms
|
||||
|
||||
msg_info "$(translate 'Installing build dependencies...')"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
@@ -91,37 +207,39 @@ install_coral_host() {
|
||||
fi
|
||||
msg_ok "$(translate 'Build dependencies installed.')"
|
||||
|
||||
|
||||
cd /tmp || exit 1
|
||||
rm -rf gasket-driver >>"$LOG_FILE" 2>&1
|
||||
msg_info "$(translate 'Cloning Google Coral driver repository...')"
|
||||
if ! git clone https://github.com/google/gasket-driver.git >>"$LOG_FILE" 2>&1; then
|
||||
msg_error "$(translate 'Could not clone the repository. Check /tmp/coral_install.log')"; exit 1
|
||||
fi
|
||||
msg_ok "$(translate 'Repository cloned successfully.')"
|
||||
|
||||
# Clone sources (feranick fork preferred, google fallback).
|
||||
# Sets GASKET_SOURCE_USED.
|
||||
clone_gasket_sources
|
||||
|
||||
cd /tmp/gasket-driver || exit 1
|
||||
msg_info "$(translate 'Patching source for kernel compatibility...')"
|
||||
|
||||
# Patch 1: 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
|
||||
# Apply compatibility patches ONLY when using the stale google/gasket-driver
|
||||
# fallback. feranick/gasket-driver already has equivalent fixes upstream, so
|
||||
# re-applying them would double-edit (and in some cases break) the sources.
|
||||
if [[ "$GASKET_SOURCE_USED" == "google" ]]; then
|
||||
msg_info "$(translate 'Patching source for kernel compatibility...')"
|
||||
|
||||
# Patch 1: 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
|
||||
fi
|
||||
|
||||
# Patch 2: MODULE_IMPORT_NS changed to string-literal syntax in kernel 6.13.
|
||||
# IMPORTANT: 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
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Source patched successfully.') (kernel ${KVER})"
|
||||
else
|
||||
msg_info2 "$(translate 'Skipping manual patches — feranick fork already supports this kernel.')"
|
||||
fi
|
||||
|
||||
# Patch 2: MODULE_IMPORT_NS changed to string-literal syntax in kernel 6.13.
|
||||
# IMPORTANT: 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
|
||||
fi
|
||||
|
||||
msg_ok "$(translate 'Source patched successfully.') (kernel ${KVER})"
|
||||
|
||||
|
||||
msg_info "$(translate 'Preparing DKMS source tree...')"
|
||||
local GASKET_SRC="/usr/src/gasket-1.0"
|
||||
# Remove any previous installation (package or manual) to avoid conflicts
|
||||
dpkg -r gasket-dkms >>"$LOG_FILE" 2>&1 || true
|
||||
# Remove any leftover manual DKMS tree from a previous run (package-level
|
||||
# cleanup was already handled by cleanup_broken_gasket_dkms above).
|
||||
dkms remove gasket/1.0 --all >>"$LOG_FILE" 2>&1 || true
|
||||
rm -rf "$GASKET_SRC"
|
||||
cp -r /tmp/gasket-driver/. "$GASKET_SRC"
|
||||
@@ -133,13 +251,16 @@ install_coral_host() {
|
||||
|
||||
msg_info "$(translate 'Compiling Coral TPU drivers for current kernel...')"
|
||||
if ! dkms build gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then
|
||||
sed -n '1,200p' /var/lib/dkms/gasket/1.0/build/make.log >>"$LOG_FILE" 2>&1 || true
|
||||
msg_error "$(translate 'DKMS build failed. Check /tmp/coral_install.log')"; exit 1
|
||||
show_dkms_build_failure
|
||||
msg_error "$(translate 'DKMS build failed.')"
|
||||
exit 1
|
||||
fi
|
||||
if ! dkms install gasket/1.0 -k "$KVER" >>"$LOG_FILE" 2>&1; then
|
||||
msg_error "$(translate 'DKMS install failed. Check /tmp/coral_install.log')"; exit 1
|
||||
show_dkms_build_failure
|
||||
msg_error "$(translate 'DKMS install failed.')"
|
||||
exit 1
|
||||
fi
|
||||
msg_ok "$(translate 'Drivers compiled and installed via DKMS.')"
|
||||
msg_ok "$(translate 'Drivers compiled and installed via DKMS.') (source: ${GASKET_SOURCE_USED})"
|
||||
|
||||
|
||||
ensure_apex_group_and_udev
|
||||
|
||||
@@ -520,6 +520,7 @@ unload_nvidia_modules() {
|
||||
}
|
||||
|
||||
complete_nvidia_uninstall() {
|
||||
msg_info "$(translate 'Completing NVIDIA uninstallation...')"
|
||||
stop_and_disable_nvidia_services
|
||||
unload_nvidia_modules
|
||||
|
||||
|
||||
Reference in New Issue
Block a user