A Linux CPUFreq governor that is schedutil plus a measured fast path for rising load: it gets bursts that follow an idle up to speed within one scheduler tick, instead of waiting for PELT to ramp up.
schedutil ramps up slowly for a reason that is easy to miss. On a CPU that is saturated (100% busy), frequency-invariant utilization can only reach the CPU's current capacity: the demand above it is censored. schedutil then raises the frequency by its 1.25x headroom at PELT's pace (a 32-period half-life of about 33.5 ms), so going from a quarter of the maximum to the maximum takes on the order of a few hundred milliseconds. Short interactive bursts, especially after an idle, finish before the frequency arrives.
Reflex keeps everything schedutil does right and adds only what PELT cannot see.
Each CPU closes, on itself, intervals of at least max(rate_limit_us, 1 tick) and measures over each one
D = busy * freq_scale * capacity (PELT's frequency-invariant units)
from kcpustat idle time and arch_scale_freq_capacity(). An interval with any idle in it shows the demand exactly. One with no idle is censored: the CPU is falling behind, and its demand is only known to be at least D.
- H, the envelope of
D: it follows a rise at once and decays exactly like PELT (decay_load()). - N, the running time seen lately, also decayed like PELT.
From N, the probability that the CPU is behind is
p = 1 after a censored interval
p = alpha / (N + alpha) otherwise, alpha = tau_min of running
The less a CPU has been seen running lately, the more likely new work on it is behind. After a long idle N has faded and p is near 1; ticks and tiny wakeups add next to nothing to N, so they cannot make a long idle look short. There is no threshold: p moves continuously with the running time observed.
util = max(1.25 * PELT, 1.25 * H), capped by uclamp_max
freq >= s*(p * lambda), s*(l) = argmin_s (P(s) + l) / s
Behind with probability p, a unit of work costs (P(s) + p * lambda) / s in energy plus latency, and s*(p * lambda) is the speed that minimizes it. s*(0) is the most efficient speed; work that is behind (p = 1) runs at s*(lambda). The floor is tabulated over p (65 points), so a frequency update only looks it up.
| Source | Where | s*(0) | s*(lambda) |
|---|---|---|---|
| Energy Model | ARM (DT, SCMI, qcom, mediatek, apple), cppc_cpufreq, and any driver that registers one |
cheapest state per unit of work | exact, from the state table |
| ACPI CPPC | amd-pstate, Intel with _CPC, ARM servers |
lowest nonlinear performance | cube-law model |
| none | anything else | 0 (as schedutil) |
cube-law model |
The floors are rebuilt only at start, on a limits change, on a latency_weight write and when the Energy Model's table is replaced; nothing is added to the update path but a table lookup.
PELT is read, never simulated. Wake-up prediction (util_est), task migration, uclamp, RT, DL and I/O wait boost all work exactly as in schedutil.
Exposed under /sys/devices/system/cpu/cpufreq/reflex/:
| Tunable | Default | Description |
|---|---|---|
rate_limit_us |
Driver default | Minimum interval between frequency updates, as in schedutil. Measurement intervals are at least max(rate_limit_us, 1 tick). |
latency_weight |
1000 | The latency weight lambda, per mille of lambda_max: the least weight at which work that is behind runs at the maximum frequency. Lower trades latency for energy; 0 keeps only the efficiency floor s*(0). Up to 100000. |
version |
(read-only) | The governor version. |
0.4.0 removes hispeed_window_us and hispeed_filter_shift.
5 ms of work (at the top speed) after an idle, on three isolated Zen 4 cores (AMD Ryzen 7 7840HS, amd-pstate passive), 24 independent trials per cell, with the governor-to-core mapping rotated every round. Median completion time; the share of runs over 7 ms in brackets.
| Idle before the burst | Reflex 0.4.0 | Reflex 0.3.3 | schedutil |
|---|---|---|---|
| 1 s | 5.53 ms (2%) | 5.54 ms (21%) | 12.93 ms (59%) |
| 100 ms | 5.52 ms (1%) | 5.62 ms (40%) | 14.50 ms (88%) |
| 16.7 ms (one 60 Hz frame) | 5.57 ms (4%) | 5.69 ms (29%) | 10.05 ms (78%) |
The ideal is about 5.2 ms. 0.3.3 and schedutil are bimodal: some runs happen to start at a stale high frequency and finish fast, the rest run slow. In an earlier, smaller run with 50 ms bursts, all three governors were within 1-2% of the ideal: the difference is in short bursts. Energy has not been measured yet.
| Patch | Base |
|---|---|
patches/0001-linux6.12.74-reflex-v0.4.0.patch |
Linux 6.12.74 (LTS) |
patches/0001-linux6.18.3-reflex-v0.4.0.patch |
Linux 6.18.3 (LTS) |
patches/0001-linux7.2.8-reflex-v0.4.0.patch |
Linux 7.2.8 |
patches/0001-linux7.3-rc1-reflex-v0.4.0.patch |
Linux 7.3-rc1 |
cpufreq_reflex.c is byte-identical in all four; the version differences live in cpufreq_reflex_compat.h and in the kernel-side exports.
cd /path/to/linux
patch -p1 < /path/to/reflex/patches/0001-linux6.12.74-reflex-v0.4.0.patchThe patch also touches the scheduler and the cpufreq core, to export what the governor needs (cpufreq_get_effective_util(), cpufreq_pelt_decay(), idle_cpu() and a few cpufreq helpers).
CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
CONFIG_CPU_FREQ_GOV_REFLEX=m
CONFIG_ENERGY_MODEL and CONFIG_ACPI_CPPC_LIB are used when present; Reflex works without them.
Build and install the whole kernel, not just the module: the exports above are part of the kernel image. Then boot it.
sudo modprobe cpufreq_reflexIf your system uses intel_pstate or amd_pstate in active mode, external governors like Reflex cannot be activated. Check the current mode:
cat /sys/devices/system/cpu/intel_pstate/status
# or
cat /sys/devices/system/cpu/amd_pstate/statusIf it reports active, switch to passive (or, for amd_pstate, guided):
echo passive | sudo tee /sys/devices/system/cpu/intel_pstate/status
# or
echo passive | sudo tee /sys/devices/system/cpu/amd_pstate/statusTo make this persistent across reboots, add a kernel boot parameter:
intel_pstate=passive
# or
amd_pstate=passive
Then activate Reflex and check it:
echo reflex | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
cat /sys/devices/system/cpu/cpufreq/reflex/version # 0.4.0
cat /sys/devices/system/cpu/cpufreq/reflex/latency_weight # 1000- Linux kernel with
CPU_FREQ,SMPandCPU_FREQ_GOV_SCHEDUTILenabled - A CPUFreq driver that uses governors:
intel_pstate/amd_pstatein passive or guided mode,acpi-cpufreq,cppc_cpufreq,cpufreq-dt,scmi-cpufreq, and so on - Standard kernel build toolchain (gcc or clang); x86_64 and arm64 are build-tested