Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

sched-ext: Extensible Scheduler

Overview

sched-ext is a Linux kernel feature that allows writing CPU schedulers as BPF programs. It enables userspace developers to implement custom scheduling policies without modifying the kernel, deploy them at runtime, and iterate rapidly. sched-ext is designed for specialized workloads (gaming, real-time, data center) that need scheduling policies beyond CFS/EEVDF.

Introduced: Linux 6.12 (commit 829e589)
Source: kernel/sched/ext.c
Kconfig: CONFIG_SCHED_CLASS_EXT=y


Architecture

flowchart TD
    subgraph UserSpace["Userspace"]
        SCHED["sched-ext scheduler<br>(BPF program)"]
        SCX["scx_utils (Rust library)"]
    end

    subgraph Kernel["Kernel"]
        EXT["sched-ext core<br>(kernel/sched/ext.c)"]
        BPF["BPF subsystem"]
        RUNQUEUE["Per-CPU runqueues"]
        DSQ["Dispatch queues (DSQ)"]
    end

    subgraph CPUs["CPUs"]
        CPU0["CPU 0"]
        CPU1["CPU 1"]
        CPUN["CPU N"]
    end

    SCX --> SCHED
    SCHED -->|BPF load| EXT
    EXT --> BPF
    EXT --> RUNQUEUE
    RUNQUEUE --> DSQ
    DSQ --> CPU0
    DSQ --> CPU1
    DSQ --> CPUN

Core Concepts

Dispatch Queues (DSQ)

sched-ext uses dispatch queues as an intermediary between the scheduler and CPUs:

DSQ TypeDescription
Global DSQShared queue, any CPU can consume
Per-CPU DSQLocal to a CPU, highest priority
Custom DSQUser-defined (e.g., per-cgroup, per-priority)

Scheduling Flow

sequenceDiagram
    participant Task as Task
    participant Select as select_task_rq_ext()
    participant Enqueue as enqueue_ext()
    participant DSQ as Dispatch Queue
    participant CPU as CPU

    Task->>Select: Task needs CPU
    Select->>Select: BPF: choose CPU
    Select->>Enqueue: Enqueue to DSQ
    Enqueue->>DSQ: BPF: pick DSQ
    DSQ->>CPU: consume_ext() picks next task
    CPU->>CPU: Task runs

BPF Scheduler Callbacks

A sched-ext scheduler implements these callbacks:

/* kernel/sched/ext.h — scheduler ops */
struct sched_ext_ops {
    /* Called when task is enqueued */
    void (*enqueue)(struct task_struct *p, u64 enq_flags);

    /* Called when task is dequeued */
    void (*dequeue)(struct task_struct *p, u64 deq_flags);

    /* Called to pick next task for CPU */
    struct task_struct *(*dispatch)(s32 cpu, struct task_struct *prev);

    /* Called to select CPU for task */
    s32 (*select_cpu)(struct task_struct *p, s32 prev_cpu, u64 wake_flags);

    /* Enable/disable task */
    void (*enable)(struct task_struct *p);
    void (*disable)(struct task_struct *p);

    /* ... */
};

Example: Simple FIFO Scheduler

// SPDX-License-Identifier: GPL-2.0
#include <scx/common.bpf.h>

char _license[] SEC("license") = "GPL";

/* Enqueue task to global DSQ */
void BPF_STRUCT_OPS(fifo_enqueue, struct task_struct *p, u64 enq_flags)
{
    scx_bpf_dispatch(p, SCX_DSQ_GLOBAL, SCX_SLICE_DFL, enq_flags);
}

/* Pick next task from CPU's local DSQ */
void BPF_STRUCT_OPS(fifo_dispatch, s32 cpu, struct task_struct *prev)
{
    scx_bpf_consume(SCX_DSQ_LOCAL);
}

s32 BPF_STRUCT_OPS(fifo_select_cpu, struct task_struct *p, s32 prev_cpu, u64 wake_flags)
{
    return scx_bpf_select_cpu_dfl(p, prev_cpu, wake_flags);
}

SCX_OPS_DEFINE(fifo_ops,
               .enqueue   = (void *)fifo_enqueue,
               .dispatch  = (void *)fifo_dispatch,
               .select_cpu = (void *)fifo_select_cpu,
               .name      = "fifo");

Scheduler Examples

scx_rusty (Rust-based)

A production-ready scheduler written in Rust using the scx_utils library:

# Build and run scx_rusty
cargo build --release
sudo ./target/release/scx_rusty

# Features:
# - Per-node scheduling (NUMA-aware)
# - cgroup-aware scheduling
# - Automatic load balancing

scx_lavd (Latency-Aware Virtual Deadline)

Optimized for interactive/gaming workloads:

# Run lavd scheduler
sudo scx_lavd

# Features:
# - Virtual deadline scheduling
# - Prioritizes latency-sensitive tasks
# - Gaming/VR optimized

Custom Scheduler Deployment

# Load a BPF scheduler
bpftool prog load my_sched.bpf.o /sys/fs/bpf/my_sched

# Switch to sched-ext
echo enabled > /sys/kernel/sched_ext/root/ops

# Check current scheduler
cat /sys/kernel/sched_ext/root/ops

# Switch back to default
echo disabled > /sys/kernel/sched_ext/root/ops

Use Cases

Use CaseSchedulerBenefit
Gaming/VRscx_lavdLow frame-time variance
Data centerscx_rustyNUMA-aware, cgroup support
Real-timeCustomDeterministic latency
Batch processingCustomThroughput optimization
Container isolationCustomPer-cgroup scheduling

Monitoring

# Check if sched-ext is active
cat /sys/kernel/sched_ext/root/ops
# (empty = not active, name = active scheduler)

# sched-ext statistics
cat /proc/sched_ext/stats

# Per-task scheduler info
cat /proc/<pid>/sched
# Shows: policy=SCHED_EXT

# Trace sched-ext events
echo 1 > /sys/kernel/debug/tracing/events/sched_ext/enable
cat /sys/kernel/debug/tracing/trace_pipe

Source Files

FileContents
kernel/sched/ext.csched-ext core
kernel/sched/ext.hsched-ext header
include/linux/sched/ext.hTask struct extensions
tools/sched_ext/Example schedulers

Further Reading


See Also

  • Scheduler — main scheduler overview
  • CFS — Completely Fair Scheduler
  • EEVDF — EEVDF scheduler
  • eBPF — BPF subsystem
  • Scheduling Domains — CPU topology