sched_ext — Overview¶
Kernel docs: docs.kernel.org/scheduler/sched-ext.html · BPF program type: BPF_PROG_TYPE_STRUCT_OPS/sched_ext_ops
Blog series: Part 15 — Write a custom scheduler in Java · Part 16 — Userspace scheduler · Part 17 — Lottery scheduler · Part 18 — bpf_for_each
Source: SchedulerBase.java · Scheduler.java
It all started when I was naive enough to propose a talk at the eBPF Summit on writing Linux schedulers in Java — then I had to implement it. The result turned out to be genuinely useful: hello-ebpf can now replace the Linux CPU scheduler with pure Java code, all as a normal jar file, with no kernel patching required.
sched_ext is a Linux scheduling class introduced in kernel 6.11 that lets you implement CPU schedulers entirely in BPF. Instead of patching the kernel, you can prototype and deploy custom scheduling policies from a user-space Java program.
Prerequisites¶
- Kernel ≥ 6.14 with
CONFIG_SCHED_CLASS_EXT=y - Verify:
ls /sys/kernel/sched_extshould exist - At most one sched_ext scheduler can be active at a time — stop any running scx service
with
systemctl stop scxbefore attaching your own.
Why sched_ext?¶
Traditional kernel schedulers require kernel patches, reboots, and deep kernel expertise. sched_ext lets you:
- Prototype scheduling algorithms in minutes
- Deploy different schedulers per workload
- Roll back instantly if a scheduler misbehaves (watchdog auto-detaches in
timeout_ms) - Ship schedulers as ordinary jar files
- Use the full JVM ecosystem — data structures, profiling, third-party libraries — in your scheduling policy

Quick start¶
The minimal requirement is two annotations and one method:
import me.bechberger.ebpf.annotations.bpf.BPF;
import me.bechberger.ebpf.annotations.bpf.Property;
import me.bechberger.ebpf.bpf.BPFProgram;
import me.bechberger.ebpf.bpf.Scheduler;
import me.bechberger.ebpf.bpf.SchedulerBase;
import me.bechberger.ebpf.bpf.sched.DispatchQueue;
import me.bechberger.ebpf.bpf.sched.EnqFlags;
import me.bechberger.ebpf.type.Ptr;
import static me.bechberger.ebpf.runtime.TaskDefinitions.task_struct;
@BPF(license = "GPL")
@Property(name = "sched_name", value = "my_scheduler")
public abstract class MyScheduler extends SchedulerBase implements Scheduler {
// Attach to the shared DSQ created by SchedulerBase.init() — no extra create needed.
final DispatchQueue shared = DispatchQueue.attach(SHARED_DSQ_ID);
@Override
public void enqueue(Ptr<task_struct> p, long enq_flags) {
shared.insertScaled(p, EnqFlags.passThrough(enq_flags));
}
public static void main(String[] args) throws Exception {
try (var sched = BPFProgram.load(MyScheduler.class)) {
sched.runSchedulerLoop(); // attach + block until Ctrl-C
}
}
}
SchedulerBase provides:
- A pre-created shared DSQ at SHARED_DSQ_ID = 0
- A default init() that creates that DSQ
- A default dispatch() that moves tasks from the shared DSQ to the local CPU queue
- A default exit() that captures the exit code for getExitCode()
You only need to override enqueue().
Loading and running¶
// Attach and block until the user presses Ctrl-C:
try (var sched = BPFProgram.load(MyScheduler.class)) {
sched.runSchedulerLoop();
}
// Closing the program atomically restores the previous scheduler.
// Or manual lifecycle (useful for tests):
try (var sched = BPFProgram.load(MyScheduler.class)) {
sched.attachScheduler();
System.out.println(sched.isSchedulerAttachedProperly()); // true
Thread.sleep(5000);
} // detaches on close
Check attachment status (e.g. verify watchdog hasn't fired):
sched.isSchedulerAttachedProperly() // reads /sys/kernel/sched_ext/root/ops
sched.waitWhileSchedulerIsAttachedProperly() // blocks until detached
Danger — scheduler bugs can hang the system
If
enqueue()never inserts a task, ordispatch()never consumes one, tasks starve and the system may become unresponsive. Always test in a VM first (e.g. viavng). Thetimeout_mswatchdog auto-detaches a misbehaving scheduler, but only after the timeout elapses — during which the system may be sluggish.
The diagram below shows how dispatching works across the kernel and userspace scheduler paths:

Next: Writing a Scheduler