Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/15-idle-coordinator.md
Fimeg 18c58fc079 docs: the day viewtop got its package, its titles and its borders
STATE.md was five days stale. It now carries what is on the phone
(viewtop r76, souveraine r333) and the four defects fixed on the glass:
per-window capture was unreachable because quickshell holds wlr handles and
the ext capture source is named by an ext handle with nothing correlating
them; titles were never republished on commit; the border was a filled slab
that relied on the client covering its middle; capture came back flipped.

Also the packaging hole, which was the important one — viewtop's PKGBUILD
existed and appeared in none of packages.yml's build lists, so nothing ever
built it. Half closed: it builds now, but a viewtop commit still does not
trigger a rebuild.

TASK-15 question 1 is answered and closed: dropping the accelerometer claim
took iio-sensor-proxy from 15.3% to 1.1%, sensors overall 17.2% to 2.6% of
a core. Consequence noted against 08(g) — confidence loses accel's +0.3.

New: 54, 55, 56, and 57/58 (Pi dock head, SMB2 OTG boost) with the USB
notes they point at.
2026-08-04 18:18:17 -04:00

140 lines
7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TASK 15 — Idle coordinator: freeze, doze, and scheduled wakeups
**Status:** open — design task. Carved 2026-07-21 from Casey's ask
("app freeze state for idle, the timeoffs and wakeups to check for
notifications") plus the measured idle burn below.
## Why (measured 2026-07-21, phone idle, screen locked)
- `culver` 9.8% CPU, 6:11 accumulated — the single biggest idle spinner
- `qs` (shell) 4.5%, `souveraine-player` 3.9%
- cluster1 peaked 54.9 °C; Casey feels it in-pocket
- The mic power pin (TASK-07) holds codec sysclk + MICB2 from
boot — invisible to `top`, real on the battery
A phone that is "off" should approach zero. Nothing on the device today
even attempts that.
## Re-measured 2026-08-04 — the biggest spinner is now OURS, and it is the sensors
41 minutes uptime, locked, screen off, viewtop session:
```
23.3% iio-sensor-proxy 9m35s of CPU in 41m of uptime
9.3% monitor-sensor --proximity --light --accel (parent: souveraine-sensord)
3-4% dbus-broker carrying the traffic between the two
```
~36% of one core, permanently, on a phone doing nothing. Dies 4448 °C, battery
41 °C, and **the SoC is nowhere near a trip point** — the shallowest passive
trip on this device is the GPU's at 85 °C. So this heat is not a thermal-policy
problem, it is a spinning userspace, and no governor will touch it.
**The cause is architectural, not a bug in either program.** `souveraine-sensord`
spawns `monitor-sensor --proximity --light --accel` and holds it open forever.
Claiming the **accelerometer** is what makes iio-sensor-proxy poll continuously —
proximity and light are cheap and event-shaped, accel is polled to derive
orientation. And per `DEVICE-STATE-MACHINE.md` §4, `accel.moving` is evidence
the machine deliberately **stopped branching on**: "motion cannot tell a pocket
from an ear from a hand". We are burning a third of a core to keep producing a
reading no rule consumes.
**The prior art is the reason the SLPI exists.** Android's sensor HAL batches:
the sensor DSP accumulates samples into a hardware FIFO and wakes the AP once
per batch (`batch(sensor, sampling_period, max_report_latency)`), with wake-up
and non-wake-up variants so a screen-off phone is not woken per sample. Polling
from userspace on the application processor, through D-Bus, defeats the entire
point of having a sensor DSP — and this device *has* one (TASK-36, TASK-34).
Three questions to settle, in this order, before any freeze work:
1. **Does anything still need the accelerometer claimed?** — **ANSWERED AND
DONE 2026-08-04** (`6b67512`). Measured on the phone, idle, screen on, same
session, one flag apart:
| `monitor-sensor` flags | iio-sensor-proxy |
|---|---|
| `--proximity --light --accel` | **15.3%** |
| `--proximity --light` | **1.1%** |
Fourteen points of a core, continuously, for the reading §4 weights least
(+0.3) and §9 calls a weak signal. The claim is what makes iio-sensor-proxy
poll; nothing else here does. `monitor-sensor` itself measured 0.8% and
sensord 0.1% — the subprocess was never the cost, the *claim* was.
The accelerometer source is dropped from `SOURCES` in
`src/bin/souveraine-sensord.rs`, along with `parse_accel`, `accel_moving`
and `MOTION_WINDOW` — all three existed only to turn orientation into a
motion edge. The 2026-07-27 stuck-`Moving(true)` finding is kept in comment
because whatever reports motion next owes a decay too.
This is not "accel is unwanted": it is that a subprocess holding a
continuous claim is the wrong way to get it. See (2).
2. **If it is wanted, does it go through the SLPI with batching** rather than
iio-sensor-proxy polling? That is TASK-36's host, and it turns a continuous
poll into an interrupt per batch.
3. **Is `monitor-sensor` the right transport at all**, or does sensord read the
IIO character device with a buffer and a trigger directly? A subprocess and a
D-Bus round trip per reading is three context switches for one number.
This is the *before* number for this task now: 36% of a core and 45 °C at idle.
## Shape of the thing
One coordinator (substrate side, likely a sessiond sibling or sessiond
itself — it already owns lock state, which IS the idle signal) driving
three tiers:
1. **Screen-off, tethered to lock.** Lock → after a grace period,
freeze the app tier: `systemctl --user freeze app-*.scope` (cgroup
freezer; requires app launches to land in per-app scopes — they may
already via uwsm/exec rules, verify). The shell, sessiond, modem
stack, and the notification path stay unfrozen. Unlock → thaw
everything, instantly.
2. **Doze.** After N min locked: stop the polling herd (culver first —
find its idle loop regardless; 10% CPU while doing nothing is a bug
on its own), drop Wi-Fi to power-save, let UPower's percentage
polling coast. The lockscreen glance data (media, agent, notify
cards) must still update on wake — cards read on unlock, they don't
need background freshness.
3. **Suspend + scheduled wakeups — the end state.** s2idle with RTC
alarms (`CLOCK_BOOTTIME_ALARM` timers in the coordinator, not
rtcwake hacks): wake every M min → thaw the network fetchers for a
bounded window (check Matrix/mail/notifications) → banner/lock-card
anything that landed (NotifyEvents seam already exists) → re-freeze,
re-suspend. Calls/SMS never wait for a poll window: modem IRQs are
hardware wake sources. Blocked-by: TASK-09 FTS suspend race
(interim-fixed, kernel-side) and the TASK-07 unpin (a pinned codec
clock across suspend is either a wake veto or a lie).
## Design questions to settle before code
- Freeze unit granularity: per-app scopes vs one `apps.slice`. A slice
freeze is one command and no bookkeeping; per-app lets Chatty stay
live for SMS-adjacent work. Likely: freeze `apps.slice`, keep an
allowlist slice (`apps-wake.slice`) for the few that may run locked.
- Who is the notification fetcher during doze? If everything that polls
is frozen, the wake window must thaw → settle → fetch → freeze; the
coordinator needs a "fetch complete" signal, not a fixed sleep.
- culver: freeze it like any app, but ALSO find the spin — freezing a
busy-loop just defers the heat to unlock time.
- Wi-Fi during suspend: WoWLAN on ath10k_snoc is unproven on this
device; assume packets do NOT wake us, only RTC + modem do. Design
for that and be pleasantly surprised otherwise.
- Interaction with charging: plugged-in can skip tiers 23 (it's the
desk-clock case; also the only time `grim`/remote debugging works).
## Acceptance
Locked overnight on battery: no app-tier CPU time accumulated, wake
windows visible in the coordinator's log at the configured cadence, an
SMS arriving mid-doze rings within seconds, and morning battery drop
measurably better than the current baseline (capture tonight's number
first — it is the before).
## Connects to
TASK-08 device-state-manager (this IS its idle half — 08 keeps the
profile/governor side), TASK-09 (suspend blocker), TASK-07's
codec pin, NotifyEvents/LockNotifyCard (the landing surface for
wake-window arrivals).