From e0c90cce3b6ba4dfc70c0b28878711cbdf20ecf9 Mon Sep 17 00:00:00 2001 From: 28allday Date: Mon, 8 Jun 2026 20:46:35 +0100 Subject: [PATCH] Harden setup script: hw-support guard, module reload, ppd-aware profile - Add Step 0 guard: bail early if kernel's acer_wmi lacks predator_v4, instead of looping forever on an impossible reboot - Preserve other acer-wmi.conf options (dedup stale line, append) rather than overwriting the whole file - Try modprobe reload to activate predator_v4 live; reboot is now only the fallback - read -rp ... || answer=n so non-interactive runs survive set -e - Set performance via powerprofilesctl when power-profiles-daemon owns the platform profile (fixes silent reset on AC/battery, persists); raw sysfs write kept as fallback - Check nvidia-powerd with is-active so a dead-but-enabled service starts - Single nvidia-smi query reused; output reports actual EC max not a hardcoded 60W Validated end-to-end on the Nitro: predator_v4=Y, ppd=performance, nvidia-powerd active, GPU at 60W. Co-Authored-By: Claude Opus 4.8 (1M context) --- setup-acer-turbo.sh | 106 ++++++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/setup-acer-turbo.sh b/setup-acer-turbo.sh index 9da8086..1021d02 100755 --- a/setup-acer-turbo.sh +++ b/setup-acer-turbo.sh @@ -15,6 +15,20 @@ fi CONF="/etc/modprobe.d/acer-wmi.conf" +# Step 0: Sanity-check that this kernel's acer_wmi supports predator_v4 +# +# Without this check, unsupported hardware (or a kernel built without +# the option) would loop forever: the script would keep asking for a +# reboot that can never make predator_v4 appear. modinfo -p lists the +# parameters the module actually accepts, so we bail out early with a +# clear message instead. +if ! modinfo -p acer_wmi 2>/dev/null | grep -q "^predator_v4:"; then + echo "Error: this kernel's acer_wmi module does not support the" + echo "predator_v4 option (or the module is missing entirely)." + echo "This script is for Acer Predator/Nitro laptops on a recent kernel." + exit 1 +fi + # Step 1: Ensure predator_v4 module option is configured # # The acer_wmi kernel module talks to the Acer laptop's WMI interface. @@ -24,30 +38,43 @@ CONF="/etc/modprobe.d/acer-wmi.conf" # # This writes a config file that the kernel reads at boot time when # loading the acer_wmi module. It only needs to be written once — after -# that the file persists across reboots. -if ! grep -qs "predator_v4=1" "$CONF" 2>/dev/null; then +# that the file persists across reboots. Any stale predator_v4 line is +# removed first so other options in the file are preserved rather than +# the whole file being overwritten. +if ! grep -qs "predator_v4=1" "$CONF"; then echo "Configuring acer_wmi with predator_v4=1 ..." - echo "options acer_wmi predator_v4=1" > "$CONF" + [[ -f "$CONF" ]] && sed -i '/predator_v4/d' "$CONF" + echo "options acer_wmi predator_v4=1" >> "$CONF" echo "Written: $CONF" fi -# Step 2: Check if predator_v4 is active (requires reboot after first run) +# Step 2: Check if predator_v4 is active, reloading the module if not # # The module option from Step 1 only takes effect when the acer_wmi -# module is loaded — which happens at boot. This step reads the live -# kernel parameter to see if predator_v4 is actually active right now. +# module is loaded. This step reads the live kernel parameter to see if +# predator_v4 is actually active right now. # -# If it shows "Y", we're good. If not, a reboot is needed so the -# kernel reloads the module with the new option. The script exits here -# on first run and asks the user to reboot, then run it again. +# If it isn't, acer_wmi is a loadable module, so in most cases we can +# simply unload and reload it to pick up the new option — no reboot +# needed. The reboot prompt is only the fallback for when the reload +# fails (e.g. the module is busy or built into the kernel). CURRENT=$(cat /sys/module/acer_wmi/parameters/predator_v4 2>/dev/null || echo "unknown") +if [[ "$CURRENT" != "Y" ]]; then + echo "predator_v4 is not active yet. Trying a module reload..." + if modprobe -r acer_wmi 2>/dev/null && modprobe acer_wmi 2>/dev/null; then + CURRENT=$(cat /sys/module/acer_wmi/parameters/predator_v4 2>/dev/null || echo "unknown") + fi +fi + if [[ "$CURRENT" != "Y" ]]; then echo "" - echo "predator_v4 is not active yet." - echo "A reboot is required for the module option to take effect." + echo "Module reload didn't activate predator_v4." + echo "A reboot is required for the option to take effect." echo "" - read -p "Reboot now? [y/N] " answer + # "|| answer=n" keeps non-interactive runs (piped input, EOF on + # stdin) from being killed by set -e mid-prompt. + read -rp "Reboot now? [y/N] " answer || answer=n if [[ "$answer" =~ ^[Yy]$ ]]; then echo "Rebooting..." systemctl reboot @@ -67,13 +94,21 @@ echo "predator_v4: active" # - balanced (default, moderate performance) # - performance (full power, fans spin up as needed) # -# Setting "performance" tells the laptop firmware to allow the CPU and -# GPU to draw more power. This is a runtime setting — it does NOT -# survive a reboot, so it needs to be set each time (or automated -# via a systemd service). -echo "performance" > /sys/firmware/acpi/platform_profile 2>/dev/null && \ - echo "Platform profile: performance" || \ - echo "Warning: Could not set platform profile" +# Omarchy ships power-profiles-daemon, which considers itself the owner +# of the platform profile — a raw sysfs write behind its back can be +# silently reset (e.g. on AC/battery events). So when the daemon is +# running we go through powerprofilesctl, which also persists the +# choice across reboots for free. The direct sysfs write is kept as a +# fallback for systems without the daemon, where it is runtime-only. +if command -v powerprofilesctl &>/dev/null && systemctl is-active --quiet power-profiles-daemon; then + powerprofilesctl set performance 2>/dev/null && \ + echo "Platform profile: performance (via power-profiles-daemon, persists)" || \ + echo "Warning: powerprofilesctl could not set the profile" +else + echo "performance" > /sys/firmware/acpi/platform_profile 2>/dev/null && \ + echo "Platform profile: performance (runtime only — re-run after reboot)" || \ + echo "Warning: Could not set platform profile" +fi # Step 4: Enable nvidia-powerd for Dynamic Boost # @@ -83,10 +118,12 @@ echo "performance" > /sys/firmware/acpi/platform_profile 2>/dev/null && \ # in a CPU-heavy compile it shifts power to the CPU. # # This step enables the service so it starts automatically on every -# boot and also starts it immediately. If the service doesn't exist -# (e.g. no NVIDIA driver installed), it's skipped gracefully. -if systemctl is-enabled nvidia-powerd &>/dev/null; then - echo "nvidia-powerd: enabled" +# boot and also starts it immediately. is-active (not is-enabled) is +# checked first so a dead-but-enabled service still gets started. If +# the service doesn't exist (e.g. no NVIDIA driver installed), it's +# skipped gracefully. +if systemctl is-active --quiet nvidia-powerd 2>/dev/null; then + echo "nvidia-powerd: running" else if systemctl enable --now nvidia-powerd &>/dev/null; then echo "nvidia-powerd: enabled and started" @@ -97,26 +134,29 @@ fi # Show current GPU power state # -# Queries nvidia-smi to display the current and maximum power limits -# so the user can verify the setup worked. After pressing the Turbo -# key a few times, the "Current Power Limit" should climb up to 60W. +# Queries nvidia-smi (once) to display the current and maximum power +# limits so the user can verify the setup worked. After pressing the +# Turbo key a few times, the "Current Power Limit" should climb up +# to 60W. echo "" -CURRENT_PL=$(nvidia-smi -q -d POWER 2>/dev/null | grep "Current Power Limit" | head -1 | awk '{print $5, $6}') -MAX_PL=$(nvidia-smi -q -d POWER 2>/dev/null | grep "Max Power Limit" | head -1 | awk '{print $5, $6}') +POWER_INFO=$(nvidia-smi -q -d POWER 2>/dev/null || true) +CURRENT_PL=$(grep "Current Power Limit" <<<"$POWER_INFO" | head -1 | awk '{print $5, $6}') +MAX_PL=$(grep "Max Power Limit" <<<"$POWER_INFO" | head -1 | awk '{print $5, $6}') echo "GPU power limit: ${CURRENT_PL:-unknown} (max: ${MAX_PL:-unknown})" echo "" echo "=== Setup complete ===" echo "" -echo "The platform profile is set to 'performance' but GPU power is" -echo "controlled by the EC (Embedded Controller) via the Turbo key." +echo "GPU power is controlled by the EC (Embedded Controller) via the" +echo "Turbo key." echo "" echo "After each reboot, press the TURBO KEY (NitroSense button) on" -echo "your keyboard to cycle through GPU power levels:" +echo "your keyboard to cycle through GPU power levels. The levels are" +echo "defined by your machine's EC — e.g. on a Nitro 5 with a 60W GPU:" echo "" -echo " 35W -> 40W -> 50W -> 60W (max)" +echo " 35W -> 40W -> 50W -> 60W" echo "" -echo "Press it 3-4 times until nvidia-smi shows 60W." +echo "Press it a few times until nvidia-smi shows ${MAX_PL:-the GPU maximum}." echo "" echo "Quick check command:" echo " nvidia-smi -q -d POWER | grep 'Current Power Limit'"