EEVDF became the default fair-class scheduler in 6.6, October 2023 (kernel.org), and the transition off CFS completed in 6.12, with the remaining pieces those LWN catalogued that spring (LWN). That's the first change of that magnitude to the fair scheduler since CFS turned up in 2.6.23 back in 2007 (kernel.org).
Most people will never notice, and that's the interesting part.
What changed¶
CFS tracked virtual runtime and picked the task with the lowest; fairness by
accumulated service. Latency was managed indirectly, through heuristics and
tunables like sched_latency_ns and wakeup preemption rules that accreted over
fifteen years.
EEVDF (Earliest Eligible Virtual Deadline First) makes latency explicit. Each task gets a virtual deadline derived from its requested time slice, and the scheduler picks the eligible task with the earliest deadline. A task that wants low latency asks for a shorter slice, and gets scheduled more often for less time each go.
The practical difference is that latency becomes a declared property instead of an emergent consequence of heuristics. That's the same move being made all over systems work right now: replacing a pile of accumulated special cases with a model.
Why tuning usually fails¶
The common failure is that the symptom is scheduling-shaped and the cause isn't. Before you touch a scheduler knob, here are the far more likely explanations:
- Lock contention. Threads are runnable but serialised. The scheduler is doing its job; your application is the bottleneck.
- I/O blocking misread as CPU starvation. A task waiting on a device isn't waiting on the scheduler.
- NUMA placement. Memory on the wrong node costs you more than any scheduling decision will ever win back.
- cgroup limits. CPU throttling via quota produces latency spikes that look just like scheduler unfairness and aren't.
- Frequency scaling. That's the governor, not the scheduler.
The diagnostic order matters. Establish whether tasks are
runnable-but-not-running (sched:sched_wakeup to sched:sched_switch latency)
before you assume the scheduler is choosing wrongly. If the tasks aren't
runnable, scheduling isn't your issue. Tuning before that check is like
replacing the gearbox because the car won't move, when nobody has looked in the
fuel tank.
Where it really is a scheduling problem¶
Narrow, and real:
- Mixed latency-critical and batch workloads on shared cores, which is what EEVDF's explicit slice request is there to address.
- Heterogeneous cores. big.LITTLE and performance/efficiency core asymmetry is a hard scheduling problem and still not fully solved; see heterogeneous by default.
- Hard real-time, which wants
SCHED_DEADLINEor a different kernel altogether rather than any amount of tuning the fair scheduler.
The more significant development is sched_ext: loading a scheduler written as
a BPF program. That turns scheduling policy into something you can iterate on
in minutes rather than across kernel releases.
What that opens up is worth spelling out, because it is a change in who is allowed to have an opinion. Scheduling policy has always been decided by a small number of kernel developers arbitrating between workloads they cannot all test, and the result is a good compromise that is nobody's optimum. With sched_ext, the person who knows the workload writes the policy. A game engine developer can ship a scheduler tuned for frame deadlines. A database vendor can ship one that understands which of its threads must not be preempted mid-latch. A build farm can run one that knows a compile job is throughput work and a test runner is latency work, and stop pretending they are the same shape.
The engineering win underneath is iteration speed. Today, trying a scheduling idea means patching the kernel, building it, rebooting a machine into it and hoping the box comes back. That is an afternoon per experiment and it is why scheduler research moves slowly. Make it a program you load and unload while the system runs, and the experiment loop goes from hours to seconds. The safety is two mechanisms, and it is worth being precise about which does what: the verifier bounds what the program can touch, and a watchdog ejects a scheduler that stalls runnable tasks or misbehaves and falls back to the default. The experiment is recoverable, not proven harmless. A loaded policy can still be slow, unfair or starvation-prone right up until the kernel pulls it. Our expectation is that more scheduling progress happens in the five years after this lands than in the fifteen before it, and for the dull reason that people can finally try things.
There is an energy argument too. Correct placement on heterogeneous cores is worth real power, which matters on a phone, on a laptop and across a fleet where the electricity bill is the second-largest line of spend. A policy that knows the workload beats a general heuristic, and now the policy can know.
Whose tuning is it¶
The chokepoint here is unglamorous: whether your workload's scheduling policy is yours or your distribution's.
Right now it is the distribution's, and nobody minds much because the default is good. sched_ext changes the stakes. Once policy is a loadable program, it becomes something that can be shipped, bundled, licensed and withheld. The plausible near future is that the people with the resources to write and maintain a good one are the hyperscalers tuning their own fleets, the large database and game vendors and the distributions. Everyone else takes what they are given, as now.
That is tolerable. What would not be is the version where loading your own policy requires a signature the platform owner controls, or where the managed platform you rent runs its scheduler and declines to run yours. The capability then exists and belongs to the operator rather than the workload owner, and the fact that it is BPF rather than a kernel patch has bought you nothing. Watch what managed Kubernetes providers permit here; that is where the answer gets set in practice, long before anybody writes it down.
The open side is in a strong position and should stay there. sched_ext is in the mainline kernel, the schedulers people are writing are published, and running your own on your own metal needs permission from nobody. Here at AxisOps that is the whole argument for owning the machine: the day the tuning matters, you want to be the one who can change it. A workload whose scheduling you cannot influence is a workload running on somebody else's terms, and you will discover that at the worst possible moment.
Start from the default¶
The default scheduler is the product of enormous amounts of tuning against an enormous variety of workloads, and your prior should be that it's right. Measure first, and expect the answer to be somewhere other than the scheduler.