Pluggable CPU schedulers
Getting started on Tumbleweed
The following should get you to install a kernel containing the "sched-ext" patch and the required toolchain to run custom CPU schedulers. In order to test that everything is set up correctly, we'll compile and run a scheduler from the repository of userspace sched-ext tools.
These directions have been tested on x86_64 only.
Enabled kernel
Adding the repository with the patched kernel:
# SCX_REPO_URI=https://download.opensuse.org/repositories/home:/ggherdovich:/sched-ext-4:/kernel-scx/standard # zypper ar $SCX_REPO_URI scx # zypper ref
Searching for "kernel-default" in that repo:
# zypper se --details --repo scx --type package /^kernel-default$/ S | Name | Type | Version | Arch | Repository ---+----------------+---------+-----------------------+--------+----------- i+ | kernel-default | package | 6.10~rc2-1.1.g12821a0 | x86_64 | scx
Eventually this package will be renamed to "kernel-scx". For the moment, it's "kernel-default" like the standard kernel, so we need to identify it by its version and git commit hash.
Installing the kernel and kernel headers, necessary to build BPF programs:
# zypper install kernel-default-6.10~rc2-1.1.g12821a0.x86_64 # zypper install kernel-default-devel-6.10~rc2-1.1.g12821a0.x86_64
Development toolchain
Essentially LLVM, the Rust compiler and libbpf.
# zypper in meson cmake-full # zypper in clang18 llvm18 # zypper in libbpf1 libbpf-devel bpftool # zypper in rustup
Your first custom scheduler
We'll clone the repository of userspace sched-ext tooling and sample schedulers, and build the entire thing:
$ git clone https://github.com/sched-ext/scx.git $ cd scx $ sudo meson setup build --prefix $HOME -D libbpf_a=disabled -D bpftool=disabled
A few things to note here:
- The option
--prefix $HOME
makes so that subsequent invocations ofmeson install
place all binaries (the schedulers we're building) at$HOME/bin
instead of the default/usr/local/bin
. This isn't necessary, but shows how to control the installation directory, which is good to know. - The variable
libbpf_a
controls static linkage to libbpf. We're setting it todisabled
, as we're opting for dynamic linkage; we've installed the shared library with the libbpf1 package, and the header files with the libbpf-devel package (see the previous section). - The variable
bpftool
controls which bpftool(8) we're using. With the valuedisabled
, we're going for the system one, installed via the bpftool package. - sudo(8) is necessary as bpftool(8) is a system binary and is stored at
/sbin
; it requires root. This is a little awkward, as now all build artifacts will be owned by root, so the following meson(1) invocations will require sudo(8) as well (meson compile
,meson install
). We'll take it for now. At least the value of$HOME
is resolved by the shell before invoking sudo(8), so the binaries will end up at the right place.
We're now ready for compilation and installation:
$ sudo meson compile -C build $ sudo meson install -C build
Be aware that in case anything goes wrong and you need to restart the build process, all is needed for cleaning up is the deletion of the build directory:
$ rm -rf build
If the build is successful, our freshly built schedulers will be placed at $HOME/bin
:
$ ls -1 $HOME/bin scx_bpfland scx_central scx_flatcg scx_lavd scx_layered scx_mitosis scx_nest scx_pair scx_qmap scx_rlfifo scx_rustland scx_rusty scx_simple scx_userland
There's quite a lot of these! They can actually be compiled separately, in case you're interested in just one, or a few. Meson(1) has an introspect
command that can be used to list build targets. It will output in JSON format; by feeding that data into a jq(1) query, we can list the interesting targets like so:
$ meson introspect --targets build \ | jq 'map(select(.type == "executable" or .type == "custom")) | .[] | .name' "cc_cflags_probe" "libbpf" "bpftool_target" "scx_utils" "scx_rustland_core" "scx_layered" "scx_mitosis" "scx_rusty" "scx_rustland" "scx_rlfifo" "scx_bpfland" "scx_lavd" "rust_scheds" "scx_simple" "scx_qmap" "scx_central" "scx_pair" "scx_flatcg" "scx_userland" "scx_nest"
For example, to build only the "scx_flatcg" scheduler, the commands are:
$ sudo meson compile -C build scx_flatcg $ sudo meson install -C build
Anyways, we're now ready to load some of our new schedulers. I hear scx_rustland is a good one:
$ sudo $HOME/bin/scx_rustland 12:35:19 [INFO] libbpf: struct_ops rustland: member cgroup_init not found in kernel, skipping it as it's set to zero 12:35:19 [INFO] libbpf: struct_ops rustland: member cgroup_exit not found in kernel, skipping it as it's set to zero 12:35:19 [INFO] libbpf: struct_ops rustland: member cgroup_prep_move not found in kernel, skipping it as it's set to zero 12:35:19 [INFO] libbpf: struct_ops rustland: member cgroup_move not found in kernel, skipping it as it's set to zero 12:35:19 [INFO] libbpf: struct_ops rustland: member cgroup_cancel_move not found in kernel, skipping it as it's set to zero 12:35:19 [INFO] libbpf: struct_ops rustland: member cgroup_set_weight not found in kernel, skipping it as it's set to zero 12:35:19 [INFO] RustLand scheduler attached - 4 CPUs 12:35:20 [INFO] min_vruntime=24297415 max_vruntime=24297415 delta=0us slice=5000us 12:35:20 [INFO] tasks=6 12:35:20 [INFO] nr_user_dispatches=13 nr_kernel_dispatches=5 12:35:20 [INFO] nr_cancel_dispatches=0 nr_bounce_dispatches=0 12:35:20 [INFO] nr_running=1 nr_waiting=0 [nr_queued=0 + nr_scheduled=0] 12:35:20 [INFO] nr_failed_dispatches=0 nr_sched_congested=0 nr_page_faults=0 [OK] 12:35:20 [INFO] Running tasks: 12:35:20 [INFO] core 0 cpu 0 pid=0 12:35:20 [INFO] core 1 cpu 1 pid=0 12:35:20 [INFO] core 2 cpu 2 pid=[self] 12:35:20 [INFO] core 3 cpu 3 pid=0 12:35:21 [INFO] min_vruntime=24297415 max_vruntime=24297415 delta=0us slice=5000us 12:35:21 [INFO] tasks=6 12:35:21 [INFO] nr_user_dispatches=13 nr_kernel_dispatches=23 12:35:21 [INFO] nr_cancel_dispatches=0 nr_bounce_dispatches=0 12:35:21 [INFO] nr_running=1 nr_waiting=0 [nr_queued=0 + nr_scheduled=0] 12:35:21 [INFO] nr_failed_dispatches=0 nr_sched_congested=0 nr_page_faults=0 [OK] 12:35:21 [INFO] Running tasks: 12:35:21 [INFO] core 0 cpu 0 pid=0 12:35:21 [INFO] core 1 cpu 1 pid=[self] 12:35:21 [INFO] core 2 cpu 2 pid=500 12:35:21 [INFO] core 3 cpu 3 pid=0 12:35:22 [INFO] min_vruntime=24297415 max_vruntime=24297415 delta=0us slice=5000us 12:35:22 [INFO] tasks=6 12:35:22 [INFO] nr_user_dispatches=13 nr_kernel_dispatches=38 12:35:22 [INFO] nr_cancel_dispatches=0 nr_bounce_dispatches=0 12:35:22 [INFO] nr_running=0 nr_waiting=0 [nr_queued=0 + nr_scheduled=0] 12:35:22 [INFO] nr_failed_dispatches=0 nr_sched_congested=0 nr_page_faults=0 [OK] 12:35:22 [INFO] Running tasks: 12:35:22 [INFO] core 0 cpu 0 pid=0 12:35:22 [INFO] core 1 cpu 1 pid=[self] 12:35:22 [INFO] core 2 cpu 2 pid=0 12:35:22 [INFO] core 3 cpu 3 pid=0 12:35:23 [INFO] min_vruntime=24297415 max_vruntime=24297415 delta=0us slice=5000us 12:35:23 [INFO] tasks=5 12:35:23 [INFO] nr_user_dispatches=13 nr_kernel_dispatches=71 12:35:23 [INFO] nr_cancel_dispatches=0 nr_bounce_dispatches=0 12:35:23 [INFO] nr_running=0 nr_waiting=0 [nr_queued=0 + nr_scheduled=0] 12:35:23 [INFO] nr_failed_dispatches=0 nr_sched_congested=0 nr_page_faults=0 [OK] 12:35:23 [INFO] Running tasks: 12:35:23 [INFO] core 0 cpu 0 pid=1498 12:35:23 [INFO] core 1 cpu 1 pid=[self] 12:35:23 [INFO] core 2 cpu 2 pid=0 12:35:23 [INFO] core 3 cpu 3 pid=0 ^C EXIT: Scheduler unregistered from user space 12:35:24 [INFO] Unregister RustLand scheduler