CPU governor and thermal stability#
Why#
By default, Linux dynamically adjusts CPU core frequency based on load, to
save power (the ondemand or schedutil governor). Every frequency
change introduces transition latency and a temperature swing, both
unwanted sources of jitter for a system aiming for time precision. The
frequency is therefore locked to maximum, permanently.
How#
Locking to maximum frequency#
cat > /etc/systemd/system/cpu-governor.service <<'EOF'
[Unit]
Description=CPU frequency governor: performance (Stratum 1)
DefaultDependencies=no
After=sysinit.target local-fs.target
Before=basic.target
[Service]
Type=oneshot
ExecStart=/bin/sh -c 'for c in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do echo performance > "$c"; done'
RemainAfterExit=true
[Install]
WantedBy=basic.target
EOF
systemctl daemon-reload
systemctl enable --now cpu-governor.serviceConfining the OS to core 0#
On top of the isolcpus=2,3 isolation from the Firmware chapter, all
generic services are explicitly confined to core 0, critical services
(chrony, phc2sys) will redefine their own affinity to core 1.
mkdir -p /etc/systemd/system.conf.d/
cat > /etc/systemd/system.conf.d/00-stratum1-affinity.conf <<'EOF'
[Manager]
CPUAffinity=0
EOF
systemctl daemon-reexecPitfalls to avoid#
- Using
DefaultCPUAffinity=instead ofCPUAffinity=. This directive doesn’t exist in systemd, a common confusion with otherDefault*=options. OnlyCPUAffinity=under[Manager]actually works (seeman systemd-system.conf). - Expecting
daemon-reexecto immediately change PID 1’s affinity. systemd’s own process (PID 1) has its CPU mask fixed at launch time by the kernel,daemon-reexecreloads the config for new services launched afterward, but doesn’t retroactively change the affinity already allocated to PID 1. A full reboot is needed to see the change applied to PID 1 itself. - Looking for
cpufrequtils. This package has been dropped from recent Debian repositories. The systemd-service-writing-directly-to-sysfs approach above depends on no third-party package.
Verify#
# "performance" governor on all 4 cores
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# Effective frequency at maximum (2.4 GHz on Pi 5)
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
# PID 1's affinity (before reboot: reflects isolcpus alone;
# after reboot: reflects isolcpus + CPUAffinity=0 combined)
grep Cpus_allowed_list /proc/1/status
# Indirect test: a service launched NOW inherits the new default
systemd-run --pipe --wait sh -c 'grep Cpus_allowed_list /proc/self/status'
# Should return "Cpus_allowed_list: 0"
systemctl show --property=CPUAffinityreturns empty at the manager level, this property is only exposed for individual units. The reliable test goes through/proc/self/statusviasystemd-run, as shown above.
Actual thermal stability (active cooling, measuring the impact on oscillator drift) is covered separately in the Active thermal stabilization chapter.