nixos/systemd-oomd: Add a new module + test

This commit is contained in:
Janne Heß 2022-04-21 19:54:50 +02:00
parent bacac7cf54
commit 3284f4fa19
No known key found for this signature in database
GPG key ID: 69165158F05265DF
6 changed files with 120 additions and 0 deletions

View file

@ -539,6 +539,21 @@
Add udev rules for the Teensy family of microcontrollers.
</para>
</listitem>
<listitem>
<para>
systemd-oomd is enabled by default. Depending on which systemd
units have <literal>ManagedOOMSwap=kill</literal> or
<literal>ManagedOOMMemoryPressure=kill</literal>, systemd-oomd
will SIGKILL all the processes under the appropriate
descendant cgroups when the configured limits are exceeded.
NixOS does currently not configure cgroups with oomd by
default, this can be enabled using
<link xlink:href="options.html#opt-systemd.oomd.enableRootSlice">systemd.oomd.enableRootSlice</link>,
<link xlink:href="options.html#opt-systemd.oomd.enableSystemSlice">systemd.oomd.enableSystemSlice</link>,
and
<link xlink:href="options.html#opt-systemd.oomd.enableUserServices">systemd.oomd.enableUserServices</link>.
</para>
</listitem>
<listitem>
<para>
The <literal>pass-secret-service</literal> package now

View file

@ -182,6 +182,15 @@ Use `configure.packages` instead.
- Add udev rules for the Teensy family of microcontrollers.
- systemd-oomd is enabled by default. Depending on which systemd units have
`ManagedOOMSwap=kill` or `ManagedOOMMemoryPressure=kill`, systemd-oomd will
SIGKILL all the processes under the appropriate descendant cgroups when the
configured limits are exceeded. NixOS does currently not configure cgroups
with oomd by default, this can be enabled using
[systemd.oomd.enableRootSlice](options.html#opt-systemd.oomd.enableRootSlice),
[systemd.oomd.enableSystemSlice](options.html#opt-systemd.oomd.enableSystemSlice),
and [systemd.oomd.enableUserServices](options.html#opt-systemd.oomd.enableUserServices).
- The `pass-secret-service` package now includes systemd units from upstream, so adding it to the NixOS `services.dbus.packages` option will make it start automatically as a systemd user service when an application tries to talk to the libsecret D-Bus API.
- There is a new module for AMD SEV CPU functionality, which grants access to the hardware.

View file

@ -1229,6 +1229,7 @@
./system/boot/systemd/journald.nix
./system/boot/systemd/logind.nix
./system/boot/systemd/nspawn.nix
./system/boot/systemd/oomd.nix
./system/boot/systemd/shutdown.nix
./system/boot/systemd/tmpfiles.nix
./system/boot/systemd/user.nix

View file

@ -0,0 +1,57 @@
{ config, lib, ... }: let
cfg = config.systemd.oomd;
in {
options.systemd.oomd = {
enable = lib.mkEnableOption "the systemd-oomd OOM killer" // { default = true; };
# Fedora enables the first and third option by default. See the 10-oomd-* files here:
# https://src.fedoraproject.org/rpms/systemd/tree/acb90c49c42276b06375a66c73673ac351025597
enableRootSlice = lib.mkEnableOption "oomd on the root slice (-.slice)";
enableSystemSlice = lib.mkEnableOption "oomd on the system slice (system.slice)";
enableUserServices = lib.mkEnableOption "oomd on all user services (user@.service)";
extraConfig = lib.mkOption {
type = with lib.types; attrsOf (oneOf [ str int bool ]);
default = {};
example = lib.literalExpression ''{ DefaultMemoryPressureDurationSec = "20s"; }'';
description = ''
Extra config options for systemd-oomd. See man oomd.conf
for available options.
'';
};
};
config = lib.mkIf cfg.enable {
systemd.additionalUpstreamSystemUnits = [
"systemd-oomd.service"
"systemd-oomd.socket"
];
systemd.services.systemd-oomd.wantedBy = [ "multi-user.target" ];
environment.etc."systemd/oomd.conf".text = lib.generators.toINI {} {
OOM = cfg.extraConfig;
};
systemd.oomd.extraConfig.DefaultMemoryPressureDurationSec = lib.mkDefault "20s"; # Fedora default
users.users.systemd-oom = {
description = "systemd-oomd service user";
group = "systemd-oom";
isSystemUser = true;
};
users.groups.systemd-oom = { };
systemd.slices."-".sliceConfig = lib.mkIf cfg.enableRootSlice {
ManagedOOMSwap = "kill";
};
systemd.slices."system".sliceConfig = lib.mkIf cfg.enableSystemSlice {
ManagedOOMSwap = "kill";
};
systemd.services."user@".serviceConfig = lib.mkIf cfg.enableUserServices {
ManagedOOMMemoryPressure = "kill";
ManagedOOMMemoryPressureLimit = "50%";
};
};
}

View file

@ -561,6 +561,7 @@ in {
systemd-networkd-ipv6-prefix-delegation = handleTest ./systemd-networkd-ipv6-prefix-delegation.nix {};
systemd-networkd-vrf = handleTest ./systemd-networkd-vrf.nix {};
systemd-nspawn = handleTest ./systemd-nspawn.nix {};
systemd-oomd = handleTest ./systemd-oomd.nix {};
systemd-shutdown = handleTest ./systemd-shutdown.nix {};
systemd-timesyncd = handleTest ./systemd-timesyncd.nix {};
systemd-misc = handleTest ./systemd-misc.nix {};

View file

@ -0,0 +1,37 @@
import ./make-test-python.nix ({ pkgs, ... }:
{
name = "systemd-oomd";
nodes.machine = { pkgs, ... }: {
systemd.oomd.extraConfig.DefaultMemoryPressureDurationSec = "1s"; # makes the test faster
# Kill cgroups when more than 1% pressure is encountered
systemd.slices."-".sliceConfig = {
ManagedOOMMemoryPressure = "kill";
ManagedOOMMemoryPressureLimit = "1%";
};
# A service to bring the system under memory pressure
systemd.services.testservice = {
serviceConfig.ExecStart = "${pkgs.coreutils}/bin/tail /dev/zero";
};
# Do not kill the backdoor
systemd.services.backdoor.serviceConfig.ManagedOOMMemoryPressure = "auto";
virtualisation.memorySize = 1024;
};
testScript = ''
# Start the system
machine.wait_for_unit("multi-user.target")
machine.succeed("oomctl")
# Bring the system into memory pressure
machine.succeed("echo 0 > /proc/sys/vm/panic_on_oom") # NixOS tests kill the VM when the OOM killer is invoked - override this
machine.succeed("systemctl start testservice")
# Wait for oomd to kill something
# Matches these lines:
# systemd-oomd[508]: Killed /system.slice/systemd-udevd.service due to memory pressure for / being 3.26% > 1.00% for > 1s with reclaim activity
machine.wait_until_succeeds("journalctl -b | grep -q 'due to memory pressure for'")
'';
})