v0.1.3 — fix power state not reverting on Gaming Mode exit
Symptom: after exiting Gaming Mode (Super+Shift+R or Steam→Power→Switch
to Desktop) the laptop stayed pinned at CPU governor=performance and
power profile=performance until manually reset. Drained battery, ran hot.
Root causes:
- The wrapper's trap-based restore raced with `systemctl restart sddm`
in switch-to-desktop. SDDM teardown could SIGKILL the wrapper before
`restore_balanced_mode` finished its writes.
- restore_balanced_mode hardcoded "powersave" / "balanced" — wrong on
systems whose default is schedutil or power-saver.
- powerprofilesctl was called without sudo and without a polkit auth
rule, so it silently failed when the session had no auth agent.
Fix:
- On entry, gamescope-session-nm-wrapper writes the user's actual
pre-Gaming-Mode CPU governor + power profile to ~/.cache/deckshift/
saved-state.
- On exit, switch-to-desktop reads that file FIRST (synchronous, before
any pkill / sddm-restart) and applies the restore. Wrapper's trap
still runs as a backup.
- Restore reads from the saved-state file rather than guessing.
- powerprofilesctl is now invoked via `sudo -n` and added to the
NOPASSWD allowlist (%video ALL=(ALL) NOPASSWD: powerprofilesctl set *)
so the call always succeeds without a polkit prompt.
Verified on Acer Nitro (AMD APU + NVIDIA hybrid):
Before: stuck at performance/performance after exit.
After: back to powersave/balanced on Switch-to-Desktop, both via the
synchronous restore in switch-to-desktop and the wrapper trap.
This commit is contained in:
parent
f2de4a3744
commit
c6bb0d9db9
1 changed files with 59 additions and 18 deletions
77
deckshift.sh
77
deckshift.sh
|
|
@ -958,6 +958,7 @@ UDEV_RULES
|
||||||
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w net.core.wmem_max=*
|
%video ALL=(ALL) NOPASSWD: /usr/bin/sysctl -w net.core.wmem_max=*
|
||||||
%video ALL=(ALL) NOPASSWD: /usr/bin/nvidia-smi -pm *
|
%video ALL=(ALL) NOPASSWD: /usr/bin/nvidia-smi -pm *
|
||||||
%video ALL=(ALL) NOPASSWD: /usr/bin/nvidia-smi -pl *
|
%video ALL=(ALL) NOPASSWD: /usr/bin/nvidia-smi -pl *
|
||||||
|
%video ALL=(ALL) NOPASSWD: /usr/bin/powerprofilesctl set *
|
||||||
SUDOERS_PERF
|
SUDOERS_PERF
|
||||||
)
|
)
|
||||||
local tee_exit=$?
|
local tee_exit=$?
|
||||||
|
|
@ -1971,31 +1972,40 @@ NVIDIA_WRAPPER
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
log() { logger -t gamescope-wrapper "$*"; echo "$*"; }
|
log() { logger -t gamescope-wrapper "$*"; echo "$*"; }
|
||||||
|
|
||||||
|
SAVED_STATE_FILE="$HOME/.cache/deckshift/saved-state"
|
||||||
|
|
||||||
|
# Capture the user's actual pre-Gaming-Mode CPU governor + power profile so we
|
||||||
|
# can restore those exact values on exit, instead of guessing "powersave/balanced"
|
||||||
|
# (which was wrong on systems whose default is schedutil or power-saver).
|
||||||
|
save_pre_gaming_state() {
|
||||||
|
mkdir -p "$(dirname "$SAVED_STATE_FILE")"
|
||||||
|
{
|
||||||
|
echo "PRE_GAMING_CPU_GOVERNOR=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor 2>/dev/null)"
|
||||||
|
echo "PRE_GAMING_POWER_PROFILE=$(powerprofilesctl get 2>/dev/null)"
|
||||||
|
} > "$SAVED_STATE_FILE"
|
||||||
|
log "Saved pre-Gaming-Mode state to $SAVED_STATE_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
enable_performance_mode() {
|
enable_performance_mode() {
|
||||||
log "Enabling performance mode..."
|
log "Enabling performance mode..."
|
||||||
|
save_pre_gaming_state
|
||||||
|
|
||||||
# Set CPU governor to performance
|
# Set CPU governor to performance
|
||||||
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
|
||||||
echo performance > "$gov" 2>/dev/null && log "CPU governor set to performance"
|
|
||||||
break # Log only once
|
|
||||||
done
|
|
||||||
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
||||||
echo performance > "$gov" 2>/dev/null
|
echo performance > "$gov" 2>/dev/null
|
||||||
done
|
done
|
||||||
|
log "CPU governor set to performance"
|
||||||
|
|
||||||
# NVIDIA dGPU performance mode
|
# NVIDIA dGPU performance mode
|
||||||
if command -v nvidia-smi &>/dev/null; then
|
if command -v nvidia-smi &>/dev/null; then
|
||||||
# Enable persistence mode (keeps GPU initialized)
|
|
||||||
sudo -n nvidia-smi -pm 1 2>/dev/null && log "NVIDIA persistence mode enabled"
|
sudo -n nvidia-smi -pm 1 2>/dev/null && log "NVIDIA persistence mode enabled"
|
||||||
|
|
||||||
# Set power limit to maximum
|
|
||||||
local max_power
|
local max_power
|
||||||
max_power=$(nvidia-smi --query-gpu=power.max_limit --format=csv,noheader,nounits 2>/dev/null | head -1 | cut -d'.' -f1)
|
max_power=$(nvidia-smi --query-gpu=power.max_limit --format=csv,noheader,nounits 2>/dev/null | head -1 | cut -d'.' -f1)
|
||||||
if [[ -n "$max_power" && "$max_power" -gt 0 ]]; then
|
if [[ -n "$max_power" && "$max_power" -gt 0 ]]; then
|
||||||
sudo -n nvidia-smi -pl "$max_power" 2>/dev/null && log "NVIDIA power limit set to ${max_power}W"
|
sudo -n nvidia-smi -pl "$max_power" 2>/dev/null && log "NVIDIA power limit set to ${max_power}W"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Prevent NVIDIA GPU runtime suspend
|
|
||||||
for nvidia_pci in /sys/bus/pci/devices/*/power/control; do
|
for nvidia_pci in /sys/bus/pci/devices/*/power/control; do
|
||||||
if [[ -f "${nvidia_pci%/power/control}/driver" ]]; then
|
if [[ -f "${nvidia_pci%/power/control}/driver" ]]; then
|
||||||
local drv=$(basename "$(readlink -f "${nvidia_pci%/power/control}/driver")" 2>/dev/null)
|
local drv=$(basename "$(readlink -f "${nvidia_pci%/power/control}/driver")" 2>/dev/null)
|
||||||
|
|
@ -2006,30 +2016,40 @@ enable_performance_mode() {
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Set power profile to performance (if power-profiles-daemon is available)
|
# Set power profile to performance — sudo -n bypasses the polkit prompt
|
||||||
|
# (NOPASSWD allowed for `powerprofilesctl set *` in /etc/sudoers.d/gaming-session-switch)
|
||||||
if command -v powerprofilesctl &>/dev/null; then
|
if command -v powerprofilesctl &>/dev/null; then
|
||||||
powerprofilesctl set performance 2>/dev/null && log "Power profile set to performance"
|
sudo -n powerprofilesctl set performance 2>/dev/null || \
|
||||||
|
powerprofilesctl set performance 2>/dev/null && log "Power profile set to performance"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
restore_balanced_mode() {
|
restore_balanced_mode() {
|
||||||
log "Restoring balanced mode..."
|
log "Restoring pre-Gaming-Mode state..."
|
||||||
|
|
||||||
# Restore CPU governor to powersave/schedutil
|
# Read what the user's state actually was before Gaming Mode; fall back
|
||||||
|
# to safe defaults if the saved-state file is missing.
|
||||||
|
local saved_cpu_gov="" saved_pp=""
|
||||||
|
if [[ -f "$SAVED_STATE_FILE" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SAVED_STATE_FILE"
|
||||||
|
saved_cpu_gov="${PRE_GAMING_CPU_GOVERNOR:-}"
|
||||||
|
saved_pp="${PRE_GAMING_POWER_PROFILE:-}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local target_gov="${saved_cpu_gov:-powersave}"
|
||||||
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
||||||
echo powersave > "$gov" 2>/dev/null
|
echo "$target_gov" > "$gov" 2>/dev/null
|
||||||
done
|
done
|
||||||
|
|
||||||
# NVIDIA dGPU restore
|
# NVIDIA dGPU restore
|
||||||
if command -v nvidia-smi &>/dev/null; then
|
if command -v nvidia-smi &>/dev/null; then
|
||||||
# Restore default power limit
|
|
||||||
local default_power
|
local default_power
|
||||||
default_power=$(nvidia-smi --query-gpu=power.default_limit --format=csv,noheader,nounits 2>/dev/null | head -1 | cut -d'.' -f1)
|
default_power=$(nvidia-smi --query-gpu=power.default_limit --format=csv,noheader,nounits 2>/dev/null | head -1 | cut -d'.' -f1)
|
||||||
if [[ -n "$default_power" && "$default_power" -gt 0 ]]; then
|
if [[ -n "$default_power" && "$default_power" -gt 0 ]]; then
|
||||||
sudo -n nvidia-smi -pl "$default_power" 2>/dev/null
|
sudo -n nvidia-smi -pl "$default_power" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Re-enable NVIDIA GPU runtime suspend (power saving)
|
|
||||||
for nvidia_pci in /sys/bus/pci/devices/*/power/control; do
|
for nvidia_pci in /sys/bus/pci/devices/*/power/control; do
|
||||||
if [[ -f "${nvidia_pci%/power/control}/driver" ]]; then
|
if [[ -f "${nvidia_pci%/power/control}/driver" ]]; then
|
||||||
local drv=$(basename "$(readlink -f "${nvidia_pci%/power/control}/driver")" 2>/dev/null)
|
local drv=$(basename "$(readlink -f "${nvidia_pci%/power/control}/driver")" 2>/dev/null)
|
||||||
|
|
@ -2039,16 +2059,18 @@ restore_balanced_mode() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Disable persistence mode (allow GPU to sleep)
|
|
||||||
sudo -n nvidia-smi -pm 0 2>/dev/null
|
sudo -n nvidia-smi -pm 0 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restore power profile to balanced
|
# Restore power profile (saved value, or balanced fallback)
|
||||||
if command -v powerprofilesctl &>/dev/null; then
|
if command -v powerprofilesctl &>/dev/null; then
|
||||||
powerprofilesctl set balanced 2>/dev/null
|
local target_pp="${saved_pp:-balanced}"
|
||||||
|
sudo -n powerprofilesctl set "$target_pp" 2>/dev/null || \
|
||||||
|
powerprofilesctl set "$target_pp" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log "Balanced mode restored"
|
rm -f "$SAVED_STATE_FILE"
|
||||||
|
log "Pre-Gaming-Mode state restored (governor=$target_gov profile=${saved_pp:-balanced})"
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
|
|
@ -2213,6 +2235,25 @@ if [[ ! -f /tmp/.gaming-session-active ]]; then
|
||||||
fi
|
fi
|
||||||
rm -f /tmp/.gaming-session-active
|
rm -f /tmp/.gaming-session-active
|
||||||
|
|
||||||
|
# SYNCHRONOUS POWER RESTORE — done first because the trap-based restore in
|
||||||
|
# gamescope-session-nm-wrapper can be SIGKILL'd by `systemctl restart sddm`
|
||||||
|
# before it completes, leaving CPU governor / power profile stuck at
|
||||||
|
# "performance". Reading the saved-state file first guarantees the user's
|
||||||
|
# pre-Gaming-Mode values are restored even if the wrapper's trap dies.
|
||||||
|
SAVED_STATE_FILE="$HOME/.cache/deckshift/saved-state"
|
||||||
|
if [[ -f "$SAVED_STATE_FILE" ]]; then
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "$SAVED_STATE_FILE"
|
||||||
|
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
|
||||||
|
echo "${PRE_GAMING_CPU_GOVERNOR:-powersave}" > "$gov" 2>/dev/null
|
||||||
|
done
|
||||||
|
if command -v powerprofilesctl &>/dev/null && [[ -n "${PRE_GAMING_POWER_PROFILE:-}" ]]; then
|
||||||
|
sudo -n powerprofilesctl set "$PRE_GAMING_POWER_PROFILE" 2>/dev/null || \
|
||||||
|
powerprofilesctl set "$PRE_GAMING_POWER_PROFILE" 2>/dev/null
|
||||||
|
fi
|
||||||
|
rm -f "$SAVED_STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
sudo -n systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null
|
sudo -n systemctl unmask sleep.target suspend.target hibernate.target hybrid-sleep.target 2>/dev/null
|
||||||
sudo -n /usr/local/bin/gaming-session-switch desktop 2>/dev/null || true
|
sudo -n /usr/local/bin/gaming-session-switch desktop 2>/dev/null || true
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue