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

7 KiB
Raw Blame History

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).