nixpkgs/nixos/modules/services/scheduling/scx.nix
eljamm 6316b883f1 nixos/scx: support temporarily overriding scheduler
One of the nicest perks of using scx is the ability to easily switch
between schedulers at run-time, but this is currently not possible in
NixOS, given that the scx service only uses the scheduler from the
config file.

It's possible, however, to override the scheduler and its flags using
the `SCX_SCHEDULER_OVERRIDE` and `SCX_FLAGS_OVERRIDE` environment
variables, respectively (see [more
details](https://en.opensuse.org/Pluggable_CPU_schedulers#Temporarily_switch_to_a_different_scheduler)).

So the solution here is to change the scx service executable and pass
those env vars to the service at run-time falling back to the values
from the module config as defaults.

After this change, it would be possible to override schedulers like so:

```shellSession
$ sudo systemctl set-environment SCX_SCHEDULER_OVERRIDE='scx_lavd'
$ sudo systemctl set-environment SCX_FLAGS_OVERRIDE='--performance'
$ sudo systemctl restart scx.service
```
```shellSession
$ journalctl -u scx.service -b 0 -e
Oct 18 13:09:25 nixos systemd[1]: Started SCX scheduler daemon.
Oct 18 13:09:25 nixos bash[1829848]: 13:09:25 [INFO] Performance mode is
enabled.
Oct 18 13:09:25 nixos bash[1829848]: 13:09:25 [INFO] Energy model won't
be used for CPU preference order.
Oct 18 13:09:26 nixos bash[1829848]: 13:09:26 [WARN] libbpf: map
'lavd_ops': BPF map skeleton link is uninitialized
Oct 18 13:09:26 nixos bash[1829848]: 13:09:26 [INFO] scx_lavd scheduler
is initialized (build ID: 1.0.16 x86_64-unknown-linux-gnu)
Oct 18 13:09:26 nixos bash[1829848]: 13:09:26 [INFO] scx_lavd scheduler
starts running.
```

And also restore to the module defaults:

```shellSession
$ sudo systemctl unset-environment SCX_SCHEDULER_OVERRIDE
$ sudo systemctl unset-environment SCX_FLAGS_OVERRIDE
$ sudo systemctl restart scx.service
```
```shellSession
$ journalctl -u scx.service -b 0 -e
Oct 18 13:12:56 nixos systemd[1]: Stopped SCX scheduler daemon.
Oct 18 13:12:56 nixos systemd[1]: Started SCX scheduler daemon.
Oct 18 13:12:56 nixos bash[1833220]: 13:12:56 [INFO] NUMA nodes: 1
Oct 18 13:12:56 nixos bash[1833220]: 13:12:56 [INFO] Disabling NUMA
optimizations
Oct 18 13:12:56 nixos bash[1833220]: 13:12:56 [INFO] scx_bpfland 1.0.16
x86_64-unknown-linux-gnu SMT on
```
2025-10-18 13:22:49 +02:00

129 lines
3.3 KiB
Nix

{
lib,
pkgs,
config,
utils,
...
}:
let
cfg = config.services.scx;
in
{
options.services.scx = {
enable = lib.mkEnableOption null // {
description = ''
Whether to enable SCX service, a daemon to run schedulers from userspace.
::: {.note}
This service requires a kernel with the Sched-ext feature.
Generally, kernel version 6.12 and later are supported.
:::
'';
};
package = lib.mkOption {
type = lib.types.package;
default = pkgs.scx.full;
defaultText = lib.literalExpression "pkgs.scx.full";
example = lib.literalExpression "pkgs.scx.rustscheds";
description = ''
`scx` package to use. `scx.full`, which includes all schedulers, is the default.
You may choose a minimal package, such as `pkgs.scx.rustscheds`.
::: {.note}
Overriding this does not change the default scheduler; you should set `services.scx.scheduler` for it.
:::
'';
};
scheduler = lib.mkOption {
type = lib.types.enum [
"scx_bpfland"
"scx_chaos"
"scx_cosmos"
"scx_central"
"scx_flash"
"scx_flatcg"
"scx_lavd"
"scx_layered"
"scx_mitosis"
"scx_nest"
"scx_p2dq"
"scx_pair"
"scx_prev"
"scx_qmap"
"scx_rlfifo"
"scx_rustland"
"scx_rusty"
"scx_sdt"
"scx_simple"
"scx_tickless"
"scx_userland"
"scx_wd40"
];
default = "scx_rustland";
example = "scx_bpfland";
description = ''
Which scheduler to use. See [SCX documentation](https://github.com/sched-ext/scx/tree/main/scheds)
for details on each scheduler and guidance on selecting the most suitable one.
'';
};
extraArgs = lib.mkOption {
type = lib.types.listOf lib.types.singleLineStr;
default = [ ];
example = [
"--slice-us 5000"
"--verbose"
];
description = ''
Parameters passed to the chosen scheduler at runtime.
::: {.note}
Run `chosen-scx-scheduler --help` to see the available options. Generally,
each scheduler has its own set of options, and they are incompatible with each other.
:::
'';
};
};
config = lib.mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
systemd.services.scx = {
description = "SCX scheduler daemon";
# SCX service should be started only if the kernel supports sched-ext
unitConfig.ConditionPathIsDirectory = "/sys/kernel/sched_ext";
startLimitIntervalSec = 30;
startLimitBurst = 2;
serviceConfig = {
Type = "simple";
ExecStart = ''
${pkgs.runtimeShell} -c 'exec ${cfg.package}/bin/''${SCX_SCHEDULER_OVERRIDE:-$SCX_SCHEDULER} ''${SCX_FLAGS_OVERRIDE:-$SCX_FLAGS}'
'';
Restart = "on-failure";
};
environment = {
SCX_SCHEDULER = cfg.scheduler;
SCX_FLAGS = lib.escapeShellArgs cfg.extraArgs;
};
wantedBy = [ "multi-user.target" ];
};
assertions = [
{
assertion = lib.versionAtLeast config.boot.kernelPackages.kernel.version "6.12";
message = "SCX is only supported on kernel version >= 6.12.";
}
];
};
meta = {
inherit (pkgs.scx.full.meta) maintainers;
};
}