Plasmoide CPU Frequency Control: governor, limiti, EPP, boost e profili per task
Widget KDE Plasma 6 (org.enne2.cpufreq) per pilotare il profilo energetico della CPU: governor, limiti min/max, energy_performance_preference, boost/turbo, quattro profili pronti (Silenzioso, Risparmio, Bilanciato, Performance) e task con profilo dedicato che ripristina lo stato precedente alla fine del comando. Privilegi separati: il widget resta non privilegiato e l'unica operazione root passa da un helper a interfaccia ristretta in /usr/local/libexec/enne2-cpufreq/ (comandi a elenco chiuso, argomenti validati su sysfs, rilettura di verifica), invocato via pkexec con l'azione polkit dedicata org.enne2.cpufreq.set. La regola in /etc/polkit-1/rules.d/ concede il consenso senza prompt a utenti locali in sessione attiva nel gruppo wheel; senza la regola la policy chiede la password. Dettagli verificati su frigate (Plasma 6.7.5, kernel 7.2, amd-pstate-epp): - con boost disattivato cpuinfo_max_freq scende alla frequenza nominale, quindi il tetto hardware si legge da amd_pstate_max_freq e i profili applicano il boost prima dei limiti; - scrittura dei limiti in ordine sicuro (mai min > max intermedio); - baseline e contatore di riferimento per i task paralleli; - syncControls protetto da uiReady e try/catch: un ReferenceError su un id della rappresentazione non deve interrompere l'aggiornamento dello stato; - implicitWidth/Height costanti, altrimenti il containment riduce il widget a 68x68. Include install.sh/uninstall.sh (widget, backend, dry-run), README con modello di sicurezza e diagnostica, screenshot docs/widget.png. Licenza MIT.
This commit is contained in:
Executable
+145
@@ -0,0 +1,145 @@
|
||||
# shellcheck shell=sh
|
||||
# Profili di frequenza condivisi fra helper privilegiato, lettore di stato e plasmoide.
|
||||
# Sorgente unica di verità: NON scrive nulla su sysfs, contiene solo la tabella dei
|
||||
# profili e l'aritmetica dei limiti (kHz). Copiato da install.sh accanto all'helper
|
||||
# in /usr/local/libexec/enne2-cpufreq/.
|
||||
#
|
||||
# Ogni profilo agisce su: governor, energy_performance_preference (EPP), boost e
|
||||
# tetto di frequenza espresso come percentuale dello span utile
|
||||
# (floor_khz .. cpuinfo_max_freq).
|
||||
|
||||
# Lettura minimale (il file è già stato validato dal chiamante dove serve).
|
||||
cf_read() {
|
||||
_cfout=
|
||||
[ -r "$1" ] || return 1
|
||||
IFS= read -r _cfout < "$1" 2>/dev/null || [ -n "$_cfout" ] || return 1
|
||||
printf '%s\n' "$_cfout"
|
||||
}
|
||||
|
||||
# Tetto hardware reale (kHz), indipendente dallo stato del boost:
|
||||
# con boost disattivato il kernel riporta in cpuinfo_max_freq la frequenza
|
||||
# nominale (es. 3.80 GHz invece di 4.97 GHz su amd-pstate). amd_pstate_max_freq
|
||||
# conserva il massimo effettivo; su altri driver cpuinfo_max_freq è già corretto.
|
||||
cf_hw_max_khz() {
|
||||
_cfp0=$1
|
||||
_cfmax=$(cf_read "$_cfp0/cpuinfo_max_freq" || echo 0)
|
||||
case "$_cfmax" in ''|*[!0-9]*) _cfmax=0 ;; esac
|
||||
_cfamd=$(cf_read "$_cfp0/amd_pstate_max_freq" || echo 0)
|
||||
case "$_cfamd" in ''|*[!0-9]*) _cfamd=0 ;; esac
|
||||
[ "$_cfamd" -gt "$_cfmax" ] && _cfmax=$_cfamd
|
||||
printf '%s\n' "$_cfmax"
|
||||
}
|
||||
|
||||
cf_profiles() {
|
||||
printf '%s\n' quiet powersave balanced performance
|
||||
}
|
||||
|
||||
cf_profile_known() {
|
||||
case "$1" in
|
||||
quiet|powersave|balanced|performance) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cf_profile_gov() {
|
||||
case "$1" in
|
||||
quiet|powersave|balanced) echo powersave ;;
|
||||
performance) echo performance ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cf_profile_epp() {
|
||||
case "$1" in
|
||||
quiet) echo power ;;
|
||||
powersave) echo balance_power ;;
|
||||
balanced) echo balance_performance ;;
|
||||
performance) echo performance ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cf_profile_boost() {
|
||||
case "$1" in
|
||||
quiet|powersave) echo 0 ;;
|
||||
balanced|performance) echo 1 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cf_profile_max_pct() {
|
||||
case "$1" in
|
||||
quiet) echo 35 ;;
|
||||
powersave) echo 60 ;;
|
||||
balanced|performance) echo 100 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cf_profile_label() {
|
||||
case "$1" in
|
||||
quiet) echo "Silenzioso" ;;
|
||||
powersave) echo "Risparmio" ;;
|
||||
balanced) echo "Bilanciato" ;;
|
||||
performance) echo "Performance" ;;
|
||||
custom) echo "Personalizzato" ;;
|
||||
*) echo "$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cf_profile_desc() {
|
||||
case "$1" in
|
||||
quiet) echo "EPP power, boost off, tetto 35% dello span" ;;
|
||||
powersave) echo "EPP balance_power, boost off, tetto 60%" ;;
|
||||
balanced) echo "EPP balance_performance, boost on, nessun tetto" ;;
|
||||
performance) echo "governor performance, EPP performance, boost on" ;;
|
||||
*) echo "" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# cf_profile_max_khz <profilo> <floor_khz> <hw_max_khz>
|
||||
cf_profile_max_khz() {
|
||||
_cfp=$(cf_profile_max_pct "$1") || return 1
|
||||
_cfl=$2
|
||||
_cfh=$3
|
||||
[ "$_cfh" -gt "$_cfl" ] || { echo "$_cfh"; return 0; }
|
||||
# un profilo al 100% non deve avere tetto: nessun arrotondamento
|
||||
[ "$_cfp" -ge 100 ] && { echo "$_cfh"; return 0; }
|
||||
cf_round50 $(( _cfl + (_cfh - _cfl) * _cfp / 100 ))
|
||||
}
|
||||
|
||||
# Arrotonda a 50 MHz: valori leggibili e stabili fra una lettura e l'altra.
|
||||
cf_round50() {
|
||||
echo $(( ($1 + 25000) / 50000 * 50000 ))
|
||||
}
|
||||
|
||||
# cf_detect_profile <governor> <epp> <boost> <max_khz> <floor_khz> <hw_max_khz>
|
||||
# Stampa il nome del profilo se lo stato coincide (tolleranza sul tetto) oppure "custom".
|
||||
cf_detect_profile() {
|
||||
_dg=$1
|
||||
_de=$2
|
||||
_db=$3
|
||||
_dmax=$4
|
||||
_dfloor=$5
|
||||
_dhwmax=$6
|
||||
_span=$(( _dhwmax - _dfloor ))
|
||||
[ "$_span" -gt 0 ] || _span=1
|
||||
_tol=$(( _span / 40 )) # 2.5% dello span
|
||||
[ "$_tol" -lt 80000 ] && _tol=80000
|
||||
for _dp in $(cf_profiles); do
|
||||
[ "$_dg" = "$(cf_profile_gov "$_dp")" ] || continue
|
||||
[ "$_db" = "$(cf_profile_boost "$_dp")" ] || continue
|
||||
_dp_max=$(cf_profile_max_khz "$_dp" "$_dfloor" "$_dhwmax")
|
||||
_diff=$(( _dmax - _dp_max ))
|
||||
[ "$_diff" -lt 0 ] && _diff=$(( -_diff ))
|
||||
[ "$_diff" -le "$_tol" ] || continue
|
||||
# L'EPP può non essere scrivibile con governor "performance": in quel caso
|
||||
# accettiamo il profilo anche se l'EPP letto non coincide.
|
||||
[ "$_de" = "$(cf_profile_epp "$_dp")" ] && { echo "$_dp"; return 0; }
|
||||
if [ "$_dg" = "performance" ] && [ "$_dp" = "performance" ]; then
|
||||
echo "$_dp"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
echo custom
|
||||
}
|
||||
Reference in New Issue
Block a user