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:
enne2
2026-10-25 20:42:52 +01:00
commit d4902b0b72
15 changed files with 2674 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
# CPU Frequency Control (org.enne2.cpufreq)
Plasmoide KDE Plasma 6 per pilotare il profilo energetico della CPU: governor,
limiti di frequenza (min/max), `energy_performance_preference` (EPP), boost/turbo,
quattro profili pronti e **task con profilo dedicato** che ripristina lo stato
precedente quando il comando finisce.
Verificato su **frigate** (Fedora 44, Plasma 6.7.5 Wayland, kernel 7.2, AMD
Ryzen 7 H 255, driver `amd-pstate-epp` in modalità `active`, 16 policy).
![widget](docs/widget.png)
## Cosa fa
| Area | Azione |
|---|---|
| Profili | **Silenzioso** (EPP power, boost off, tetto 35%), **Risparmio** (balance_power, boost off, tetto 60%), **Bilanciato** (balance_performance, boost on, nessun tetto), **Performance** (governor performance, EPP performance, boost on) |
| Governor | pillole con i valori realmente disponibili in `scaling_available_governors` |
| EPP | pillole con i valori di `energy_performance_available_preferences` |
| Limiti | due slider min/max in kHz con validazione e rilettura; il valore applicato è arrotondato a 10 MHz |
| Boost | pillole `on`/`off` (per-policy su kernel recenti, altrimenti globale, altrimenti `intel_pstate/no_turbo`) |
| Task | elenco configurabile: ogni task applica un profilo, esegue il comando **come utente** e ripristina la baseline all'uscita |
| Footer | messaggio dell'ultima azione e pulsante **Reset** (default hardware: boost on, nessun tetto) |
La vista compatta (pannello) mostra frequenza corrente e un badge `no boost`.
La frequenza è la massima fra le policy (core più attivo), con la media sotto.
## Architettura e modello di sicurezza
```
org.enne2.cpufreq (QML, non privilegiato)
├─ contents/scripts/cpufreq-state.sh lettura sysfs, /proc, hwmon (nessun privilegio)
├─ contents/scripts/enne2-cpufreq-task launcher task (utente)
└─ pkexec /usr/local/libexec/enne2-cpufreq/cpufreq-helper (root)
├─ comandi a elenco chiuso, argomenti validati contro sysfs
├─ ogni scrittura riletta per verifica
└─ /var/lib/enne2-cpufreq/{baseline,refcount} (root:root 0755/0644)
polkit
├─ /usr/share/polkit-1/actions/org.enne2.cpufreq.set.policy azione dedicata
└─ /etc/polkit-1/rules.d/50-org.enne2.cpufreq.rules allow senza prompt
```
- Il widget non è mai root: l'unica operazione privilegiata è l'helper, con
sottocomandi fissi (`set-governor`, `set-epp`, `set-limits`, `set-boost`,
`apply`, `reset`, `task-begin`, `task-end`, `save-state`, `restore-state`).
- L'annotazione `org.freedesktop.policykit.exec.path` lega l'azione polkit a quel
solo binario: nessun altro programma eredita il permesso.
- La regola in `/etc/polkit-1/rules.d/` concede `polkit.Result.YES` solo a utenti
**locali**, con sessione **attiva**, nel gruppo **wheel**. Senza quel file la
`.policy` chiede la password a ogni modifica (`auth_admin_keep`).
- I comandi dei task girano come utente (`setsid sh -c`), mai come root.
## Installazione
```sh
cd ~/Dev/kde-cpufreq-dashboard
./install.sh # widget (utente) + backend (sudo)
./install.sh --widget # solo il plasmoide
./install.sh --backend # solo helper + polkit (root)
./install.sh --no-polkit-rule # backend senza la regola "nessun prompt"
./install.sh --dry-run # mostra le operazioni
```
Il widget si aggiunge da *Aggiungi widget → «CPU Frequency Control»* oppure via
scripting Plasma:
```js
desktops()[0].addWidget("org.enne2.cpufreq")
```
Disinstallazione: `./uninstall.sh` (ripristina la baseline prima di rimuovere
l'helper). I task configurati restano in `~/.config/enne2-cpufreq/tasks.json`.
## Task con profilo dedicato
Dall'interfaccia: **Nuovo** → nome, comando, profilo. Da terminale:
```sh
enne2-cpufreq-task list
enne2-cpufreq-task add "Render Blender" performance "blender -b scena.blend -f 1"
enne2-cpufreq-task run render-blender
enne2-cpufreq-task stop render-blender
enne2-cpufreq-task active
```
Sequenza di `run`:
1. `helper task-begin <profilo>`: salva la baseline (solo al primo task) e applica il profilo
2. il comando parte staccato (`setsid`) e scrive il proprio pid in
`$XDG_RUNTIME_DIR/enne2-cpufreq/<slug>.pid`
3. un watcher attende l'uscita del pid e chiama `helper task-end`, che decrementa
un contatore e ripristina la baseline a zero (con `notify-send` di conferma)
Più task in parallelo sono contati: il ripristino avviene solo quando l'ultimo
termina. `enne2-cpufreq-task remove` rifiuta di rimuovere un task in esecuzione.
## Comportamento sui driver
- **amd-pstate-epp (active)**: `powersave`/`performance` sono policy del driver;
`powersave` *non* blocca la frequenza al minimo, EPP è il vero bias.
Con `boost=0` il kernel abbassa `cpuinfo_max_freq` alla frequenza nominale
(3.80 GHz invece di 4.97 GHz): il tetto reale viene letto da
`amd_pstate_max_freq`, e per questo `apply` imposta **prima** il boost e poi i
limiti.
- **intel_pstate**: boost tramite `intel_pstate/no_turbo` (invertito).
- **Altri driver cpufreq**: governor e limiti restano validi; EPP e boost
compaiono solo se le interfacce esistono.
## Servizi concorrenti
Se `tuned`, `power-profiles-daemon` o `tlp` sono attivi il widget lo segnala: il
profilo `balanced` di tuned scrive esattamente `governor`, `energy_performance_preference`
e `boost`, quindi un `tuned-adm profile` o il boot possono sovrascrivere le
impostazioni. Su frigate `tuned` è attivo: le modifiche valgono fino al prossimo
apply di tuned.
## Diagnosi
```sh
/usr/local/libexec/enne2-cpufreq/cpufreq-helper status # stato, baseline, refcount
~/.local/share/plasma/plasmoids/org.enne2.cpufreq/contents/scripts/cpufreq-state.sh
pkexec /usr/local/libexec/enne2-cpufreq/cpufreq-helper apply balanced
journalctl --user -u plasma-plasmashell.service -f | grep cpufreq
```
| Sintomo | Causa |
|---|---|
| banner rosso «Backend non installato» | manca `/usr/local/libexec/enne2-cpufreq/` → `./install.sh --backend` |
| dialog password a ogni modifica | manca la regola polkit (o utente fuori da `wheel`) |
| «autorizzazione negata da polkit» | regola non caricata: `systemctl try-reload-or-restart polkit` |
| valori che tornano indietro | servizio concorrente (tuned/power-profiles-daemon/tlp) |
| il widget resta 68×68 | `implicitWidth/Height` del plasmoide non validi: verificare `main.qml` |
## Note di implementazione Plasma 6
- `metadata.json` con `X-Plasma-API-Minimum-Version: "6.0"`, senza chiavi Plasma 5.
- Comandi via `org.kde.plasma.plasma5support` (`engine: "executable"`); ogni
azione usa un source univoco (contatore in coda) perché il motore non riesegue
un source già noto.
- `syncControls()` è protetto: `applyState()` gira ogni 2 s e la rappresentazione
piena può non esistere (popup chiuso) → senza guardia il `ReferenceError` su un
id interrompeva la sincronizzazione dei controlli.
- `implicitWidth/implicitHeight` del `Flickable` sono costanti: se dipendono dal
layout il containment riduce il widget alla dimensione minima (68×68).
- Nessun `Switch` per il boost: `toggled` scatta anche quando il valore viene
riallineato dallo stato. Le pillole sono prive di stato interno.
## File
```
metadata.json
contents/ui/main.qml interfaccia (compact + full, Flickable)
contents/ui/ChoicePill.qml pillola selezionabile
contents/scripts/profiles.sh tabella profili condivisa (unica fonte)
contents/scripts/cpufreq-state.sh lettura stato non privilegiata
contents/scripts/enne2-cpufreq-task launcher task (utente)
bin/cpufreq-helper helper privilegiato (root)
polkit/*.policy, polkit/*.rules azione e regola polkit
install.sh, uninstall.sh
```
Licenza MIT.