nixos/iosched: init module
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
|
||||
let
|
||||
cfg = config.hardware.block;
|
||||
escape = lib.strings.escape [ ''"'' ];
|
||||
udevValue = types.addCheck types.nonEmptyStr (x: builtins.match "[^\n\r]*" x != null) // {
|
||||
name = "udevValue";
|
||||
description = "udev rule value";
|
||||
descriptionClass = "noun";
|
||||
};
|
||||
|
||||
inherit (lib)
|
||||
mkIf
|
||||
mkOption
|
||||
types
|
||||
|
||||
concatLines
|
||||
concatStringsSep
|
||||
mapAttrsToList
|
||||
optional
|
||||
;
|
||||
in
|
||||
{
|
||||
options.hardware.block = {
|
||||
defaultScheduler = mkOption {
|
||||
type = types.nullOr udevValue;
|
||||
default = null;
|
||||
description = ''
|
||||
Default block I/O scheduler.
|
||||
|
||||
Unless `null`, the value is assigned through a udev rule matching all
|
||||
block devices.
|
||||
'';
|
||||
example = "kyber";
|
||||
};
|
||||
|
||||
defaultSchedulerRotational = mkOption {
|
||||
type = types.nullOr udevValue;
|
||||
default = null;
|
||||
description = ''
|
||||
Default block I/O scheduler for rotational drives (e.g. hard disks).
|
||||
|
||||
Unless `null`, the value is assigned through a udev rule matching all
|
||||
rotational block devices.
|
||||
|
||||
This option takes precedence over
|
||||
{option}`config.hardware.block.defaultScheduler`.
|
||||
'';
|
||||
example = "bfq";
|
||||
};
|
||||
|
||||
scheduler = mkOption {
|
||||
type = types.attrsOf udevValue;
|
||||
default = { };
|
||||
description = ''
|
||||
Assign block I/O scheduler by device name pattern.
|
||||
|
||||
Names are matched using the {manpage}`udev(7)` pattern syntax:
|
||||
|
||||
`*`
|
||||
: Matches zero or more characters.
|
||||
|
||||
`?`
|
||||
: Matches any single character.
|
||||
|
||||
`[]`
|
||||
: Matches any single character specified in the brackets. Ranges are
|
||||
supported via the `-` character.
|
||||
|
||||
`|`
|
||||
: Separates alternative patterns.
|
||||
|
||||
|
||||
Please note that overlapping patterns may produce unexpected results.
|
||||
More complex configurations requiring these should instead be specified
|
||||
directly through custom udev rules, for example via
|
||||
[{option}`config.services.udev.extraRules`](#opt-services.udev.extraRules),
|
||||
to ensure correct ordering.
|
||||
|
||||
Available schedulers depend on the kernel configuration but modern
|
||||
Linux systems typically support:
|
||||
|
||||
`none`
|
||||
: No‐operation scheduler with no re‐ordering of requests. Suitable
|
||||
for devices with fast random I/O such as NVMe SSDs.
|
||||
|
||||
[`mq-deadline`](https://www.kernel.org/doc/html/latest/block/deadline-iosched.html)
|
||||
: Simple latency‐oriented general‐purpose scheduler.
|
||||
|
||||
[`kyber`](https://www.kernel.org/doc/html/latest/block/kyber-iosched.html)
|
||||
: Simple latency‐oriented scheduler for fast multi‐queue devices
|
||||
like NVMe SSDs.
|
||||
|
||||
[`bfq`](https://www.kernel.org/doc/html/latest/block/bfq-iosched.html)
|
||||
: Complex fairness‐oriented scheduler. Higher processing overhead,
|
||||
but good interactive response, especially with slower devices.
|
||||
|
||||
|
||||
Schedulers assigned through this option take precedence over
|
||||
{option}`config.hardware.block.defaultScheduler` and
|
||||
{option}`config.hardware.block.defaultSchedulerRotational` but may be
|
||||
overridden by other udev rules.
|
||||
'';
|
||||
example = {
|
||||
"mmcblk[0-9]*" = "bfq";
|
||||
"nvme[0-9]*" = "kyber";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config =
|
||||
mkIf
|
||||
(cfg.defaultScheduler != null || cfg.defaultSchedulerRotational != null || cfg.scheduler != { })
|
||||
{
|
||||
services.udev.packages = [
|
||||
(pkgs.writeTextDir "etc/udev/rules.d/98-block-io-scheduler.rules" (
|
||||
concatLines (
|
||||
map (concatStringsSep ", ") (
|
||||
optional (cfg.defaultScheduler != null) [
|
||||
''SUBSYSTEM=="block"''
|
||||
''ACTION=="add|change"''
|
||||
''TEST=="queue/scheduler"''
|
||||
''ATTR{queue/scheduler}="${escape cfg.defaultScheduler}"''
|
||||
]
|
||||
++ optional (cfg.defaultSchedulerRotational != null) [
|
||||
''SUBSYSTEM=="block"''
|
||||
''ACTION=="add|change"''
|
||||
''ATTR{queue/rotational}=="1"''
|
||||
''TEST=="queue/scheduler"''
|
||||
''ATTR{queue/scheduler}="${escape cfg.defaultSchedulerRotational}"''
|
||||
]
|
||||
++ mapAttrsToList (name: sched: [
|
||||
''SUBSYSTEM=="block"''
|
||||
''ACTION=="add|change"''
|
||||
''KERNEL=="${escape name}"''
|
||||
''ATTR{queue/scheduler}="${escape sched}"''
|
||||
]) cfg.scheduler
|
||||
)
|
||||
)
|
||||
))
|
||||
];
|
||||
};
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ mvs ];
|
||||
}
|
||||
@@ -72,6 +72,7 @@
|
||||
./hardware/i2c.nix
|
||||
./hardware/infiniband.nix
|
||||
./hardware/inputmodule.nix
|
||||
./hardware/iosched.nix
|
||||
./hardware/keyboard/qmk.nix
|
||||
./hardware/keyboard/teck.nix
|
||||
./hardware/keyboard/uhk.nix
|
||||
|
||||
@@ -488,6 +488,7 @@ in {
|
||||
honk = runTest ./honk.nix;
|
||||
installed-tests = pkgs.recurseIntoAttrs (handleTest ./installed-tests {});
|
||||
invidious = handleTest ./invidious.nix {};
|
||||
iosched = handleTest ./iosched.nix {};
|
||||
isolate = handleTest ./isolate.nix {};
|
||||
livebook-service = handleTest ./livebook-service.nix {};
|
||||
pyload = handleTest ./pyload.nix {};
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import ./make-test-python.nix (
|
||||
{
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
qemu-img = pkgs.lib.getExe' pkgs.vmTools.qemu "qemu-img";
|
||||
empty = pkgs.runCommand "empty.qcow2" { } ''
|
||||
${qemu-img} create -f qcow2 "$out" 32M
|
||||
'';
|
||||
in
|
||||
{
|
||||
name = "iosched";
|
||||
meta.maintainers = with pkgs.lib.maintainers; [ mvs ];
|
||||
|
||||
nodes.machine = {
|
||||
virtualisation.qemu.options = [
|
||||
"-drive"
|
||||
"id=sda,if=none,format=qcow2,readonly=on,file=${empty}"
|
||||
"-drive"
|
||||
"id=sdb,if=none,format=qcow2,readonly=on,file=${empty}"
|
||||
"-drive"
|
||||
"id=nvme0n1,if=none,format=qcow2,readonly=on,file=${empty}"
|
||||
"-drive"
|
||||
"id=mmcblk0,if=none,format=qcow2,file=./mmcblk0.qcow2"
|
||||
"-device"
|
||||
"virtio-scsi-pci,id=scsi0"
|
||||
"-device"
|
||||
"sdhci-pci"
|
||||
"-device"
|
||||
"scsi-hd,rotation_rate=1,bus=scsi0.0,drive=sda"
|
||||
"-device"
|
||||
"scsi-hd,rotation_rate=7200,bus=scsi0.0,drive=sdb"
|
||||
"-device"
|
||||
"sd-card,drive=mmcblk0"
|
||||
"-device"
|
||||
"nvme,serial=deadbeef,drive=nvme0n1"
|
||||
];
|
||||
|
||||
hardware.block = {
|
||||
defaultScheduler = "none";
|
||||
defaultSchedulerRotational = "mq-deadline";
|
||||
scheduler = {
|
||||
"nvme[0-9]*" = "kyber";
|
||||
"mmcblk[0-9]*" = "bfq";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import subprocess
|
||||
|
||||
def check_scheduler(dev, scheduler):
|
||||
machine.succeed("grep -F -q '[{}]' /sys/block/{}/queue/scheduler".format(scheduler, dev))
|
||||
|
||||
subprocess.check_call([
|
||||
"${qemu-img}", "create", "-f", "qcow2", "vm-state-machine/mmcblk0.qcow2", "32M"
|
||||
])
|
||||
|
||||
machine.start()
|
||||
machine.succeed("udevadm verify --no-style")
|
||||
check_scheduler("sda", "none")
|
||||
check_scheduler("sdb", "mq-deadline")
|
||||
check_scheduler("nvme0n1", "kyber")
|
||||
check_scheduler("mmcblk0", "bfq")
|
||||
'';
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user