A watch that died left the block silent, a removed block kept its value, and a value frozen at its source looked fresh because its file was new. The follower now rereads a silent value and says so, drops removed blocks, and dates each file by the reading's own time. The path check takes /etc and /usr/local/lib in, ignores what a container sees inside itself, and lists the five findings standing today, which may only go.
134 lines
8.5 KiB
Markdown
134 lines
8.5 KiB
Markdown
# power
|
|
|
|
A machine's power (novox/hq ADR 0211). This module holds `node-power` on every machine, servers
|
|
included, because every machine boots and shuts down. It owns logind's power key and lid handling. It
|
|
runs the code other modules contribute for the power moments, and it publishes the machine's power
|
|
states on the bus.
|
|
|
|
## What it owns
|
|
|
|
| | |
|
|
|---|---|
|
|
| `/etc/systemd/logind.conf.d/power.conf` | the power key and the lid, from this module's settings (below); logind is reloaded, never restarted |
|
|
| `/etc/mesh-power/moments/<moment>` | for each moment, every module's code as the controller placed it (`${shell:<moment>:<slot>}`) |
|
|
| `/usr/local/lib/mesh-power/bin/power-moment` | the runner: each module's piece on its own, as root, with `sh`, bounded (`MESH_POWER_BOUND`, default 30 s); every outcome in the journal under `mesh-power` |
|
|
| five units | `mesh-power-after-boot`, `-before-shutdown` (its stop is the shutdown, while the network is still up), `-before-sleep`, `-after-wake`, `-supply` |
|
|
| `mesh-power-draw` (process) | samples the power drawn for the module's bundle, which puts it on the bus as `power.draw` (below) |
|
|
| `/etc/udev/rules.d/90-mesh-power.rules` | a power supply's change starts `mesh-power-supply`, which runs `on-mains` or `on-battery` once per change of source |
|
|
|
|
## The moments, and how a module adds code
|
|
|
|
The moments are `after-boot`, `before-sleep`, `after-wake`, `before-shutdown`, `on-mains` and
|
|
`on-battery`. A module declares a `shell` entry whose `for` names the moment, in the `first`,
|
|
`normal` or `last` slot, with POSIX shell code. That entry depends on this seat (ADR 0210). The code
|
|
runs as root. Code that needs the operator's session finds it itself, as the laptop module's touchpad
|
|
reset does with `runuser`.
|
|
|
|
**One rule for contributed code:** the runner splits the placed file at lines of the form `# <module>`,
|
|
which the controller writes before each module's piece. A comment line of the piece's own that is
|
|
one lowercase word would split it too, so comments in contributed code use more than one word.
|
|
|
|
Code that must run *inside* the sleep transaction, such as the NVIDIA driver's own suspend and
|
|
resume units, is not a contribution. Its module keeps its own drop-ins on the sleep services (ADR
|
|
0211, "What got harder").
|
|
|
|
## The bar's battery and power draw (novox/hq ADR 0255, ADR 0260)
|
|
|
|
The module holds the machine's power everywhere, so it gives the bar what is generic about power, on
|
|
the machines that have it. Both are contributions to `node-bar` in the bar's own neutral terms, so any
|
|
bar renders them, and each names the capability the node-engine reports for it:
|
|
|
|
| block | where | what it shows |
|
|
|---|---|---|
|
|
| battery | `if-capability: battery` | the bar's own battery block: the bar reads the kernel's power-supply class itself, not anything of this module's |
|
|
| power draw | `if-capability: power-meter` | this module's state `power.draw` for the machine, over the bus |
|
|
|
|
The power draw says what it measured:
|
|
|
|
- **on battery**, the battery's discharge: `18.2 W machine`;
|
|
- **otherwise**, the processor package's power from the kernel's powercap energy counter (RAPL, Intel
|
|
and AMD), plus a discrete graphics device's own power where it reports one: `14.0 W CPU+GPU` or
|
|
`14.0 W CPU`. That is part of the machine, and is called that part.
|
|
- **The machine at the wall** needs a metered plug or a UPS and is not measured.
|
|
|
|
**Over the bus, not through a file.** Modules talk over the bus. The energy counter is root's to read,
|
|
on purpose: read often by anyone, it leaks what the processor is doing (the PLATYPUS attack), so no
|
|
permission is loosened. Two parts of this module carry the reading:
|
|
|
|
1. the process `mesh-power-draw` (`power draw`, as root) samples every 5 s and leaves the averaged rate
|
|
in `/run/mesh-power/draw`, this module's own directory, read by nothing but this module;
|
|
2. the module's bundle, which the runtime launches, puts each fresh reading in its state `draw`, keyed by
|
|
the machine's name: `{"text", "watts", "measures", "at"}`. A value lives 30 s (`ttl-seconds`). The
|
|
state is `per-machine`: each machine's instance is granted its own machine's key and no other.
|
|
|
|
**State, not events, by bus load.** A reading is the current value and is replaced every five seconds;
|
|
nobody needs the ones before. A key per machine keeps one value, so the bus holds at most one reading
|
|
per machine, and a bar starting later reads it at once. An event every five seconds would keep each one
|
|
in a stream. On four machines the puts are under one message a second.
|
|
|
|
The bar's holder is granted the read by the mesh, because this module offers the state in its block; it
|
|
names no module. Graphics are counted honestly: an AMD device only when it has its own memory (an APU's
|
|
integrated graphics report the whole package, which the counter already holds), and only while it is
|
|
awake; an NVIDIA device through `nvidia-smi` only while it is awake. Each package is counted once by its
|
|
name: where Intel shows one twice (`intel-rapl:N` and `intel-rapl-mmio:N`), the first is read. A counter
|
|
that gives more than 1000 W was reset, after a resume say, and that sample gives no reading.
|
|
|
|
**The sampler runs wherever this module is assigned, servers included.** A server has the energy
|
|
counter too, so conditioning it on `power-meter` would not keep it off one, and the mesh has no way yet
|
|
to condition a resource on another seat (the bar) being held on the machine. It costs one process
|
|
reading a few kernel files every five seconds (novox/hq ADR 0255 §9).
|
|
|
|
## Settings
|
|
|
|
`handle-power-key`, `handle-lid-switch`, `handle-lid-switch-external-power` and
|
|
`handle-lid-switch-docked`, each one of logind's actions. They have no defaults, so the mesh's layer
|
|
must be set before the first assignment. The mesh's layer carries logind's own defaults (`poweroff`,
|
|
`suspend`, `suspend`, `ignore`). A machine's layer changes them: the laptop suspends on all four.
|
|
|
|
## Events
|
|
|
|
| event | when |
|
|
|---|---|
|
|
| `booted` | once per boot, not per restart of the runtime (the boot id is remembered) |
|
|
| `sleeping` | before the machine sleeps: the module's watcher holds logind's delay lock, publishes, waits for the bus at most 3 s, and lets go |
|
|
| `woke` | after waking, queued until the bus is reachable |
|
|
| `shutting-down` | before a shutdown, as `sleeping` |
|
|
| `on-mains`, `on-battery` | when the power source changes, on a machine with a Mains supply |
|
|
| `battery-low` | at 10 % on battery, once per discharge |
|
|
|
|
Each carries the time it happened. Events the bus did not take wait in order and go out when it answers
|
|
again. A machine that said `sleeping` is asleep, not out of touch.
|
|
|
|
## Tools
|
|
|
|
| tool | |
|
|
|---|---|
|
|
| `power_state` | boot time, source and battery, lid, logind's settings, and the watcher's lock, last sleep and wake, and queue |
|
|
| `power_hooks` | every moment's contributed pieces, with their module |
|
|
| `power_history` | boots, sleeps, wakes and moment runs from the journal |
|
|
| `power_run` | run one moment now, to test it (needs `sudo -n`) |
|
|
| `power_check` | units, runner, moment files, one logind writer, no hand-placed sleep hooks, the lock held, nothing stuck before the bus |
|
|
|
|
## Migration
|
|
|
|
- **The laptop:** its model module owned `logind.conf.d/power.conf` and a touchpad resume unit. Both
|
|
move here: the file to this module, and the resume to an `after-wake` contribution.
|
|
- **The desktop:** the predecessor left `logind.conf.d/brightness.conf` and
|
|
`/etc/systemd/system-sleep/xinput-reset-touchpad.sh`. Move both aside after the first push;
|
|
`power_check` names them while they remain.
|
|
- **The servers** have no logind drop-ins and no sleep hooks today.
|
|
|
|
The lesson from the day this module was written belongs to the dbus module: a full upgrade restarted
|
|
the system bus live on a workstation, and logins hung until a reboot.
|
|
|
|
**Why the sleep hooks are wanted, not enabled.** The host reads a one-shot unit that is not running
|
|
and has not failed as having run and worked, so a hook unit waiting for a sleep could never be
|
|
declared stopped. The first version tried that and was refused on its first apply. The two sleep
|
|
units are therefore not services the mesh manages. They have no install section, and the sleep
|
|
targets want them through this module's drop-ins (`sleep.target.d`, and `suspend.target.d` with
|
|
its three siblings) — the same way the laptop module asks for the NVIDIA driver's sleep actions.
|
|
|
|
**polkit is this module's package.** logind gives a delay lock to a process that is not root only
|
|
through polkit. A server without polkit refused the watcher with "Permission denied", and its
|
|
sleeps and shutdowns would have gone unsaid. The module installs it; nothing else needed it there.
|