]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test: let kernel OOM-kill a child process instead of the main one
authorFrantisek Sumsal <frantisek@sumsal.cz>
Thu, 16 Oct 2025 11:06:51 +0000 (13:06 +0200)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 17 Oct 2025 13:19:16 +0000 (15:19 +0200)
This test occasionally fails due to a race where systemd processes
kernel's SIGKILL before the OOM notification, so the test service dies
with Result=signal instead of the expected Result=oom-kill:

[   51.008765] TEST-55-OOMD.sh[906]: + systemd-run --wait --unit oom-kill -p OOMPolicy=kill -p Delegate=yes -p DelegateSubgroup=init.scope /tmp/script.sh
[   51.048747] TEST-55-OOMD.sh[907]: Running as unit: oom-kill.service; invocation ID: 456645347d554ea2878463404b181bd8
[   51.066296] sysrq: Manual OOM execution
[   51.066596] kworker/1:0 invoked oom-killer: gfp_mask=0xcc0(GFP_KERNEL), order=-1, oom_score_adj=0
[   51.066915] CPU: 1 UID: 0 PID: 27 Comm: kworker/1:0 Not tainted 6.17.1-arch1-1 #1 PREEMPT(full)  d2b229857b2eb4001337041f41d3c4f131433540
[   51.066919] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS Arch Linux 1.17.0-2-2 04/01/2014
[   51.066921] Workqueue: events moom_callback
[   51.066928] Call Trace:
[   51.066931]  <TASK>
[   51.066936]  dump_stack_lvl+0x5d/0x80
[   51.066942]  dump_header+0x43/0x1aa
<...snip...>
[   51.087814] 47583 pages reserved
[   51.087969] 0 pages cma reserved
[   51.088208] 0 pages hwpoisoned
[   51.088519] Out of memory: Killed process 908 (sleep) total-vm:3264kB, anon-rss:256kB, file-rss:1916kB, shmem-rss:0kB, UID:0 pgtables:44kB oom_score_adj:1000
[   51.090263] TEST-55-OOMD.sh[907]:           Finished with result: signal
[   51.094416] TEST-55-OOMD.sh[907]: Main processes terminated with: code=killed, status=9/KILL
[   51.094898] TEST-55-OOMD.sh[907]:                Service runtime: 58ms
[   51.095436] TEST-55-OOMD.sh[907]:              CPU time consumed: 22ms
[   51.095854] TEST-55-OOMD.sh[907]:                    Memory peak: 1.6M (swap: 0B)
[   51.096722] TEST-55-OOMD.sh[912]: ++ systemctl show oom-kill -P Result
[   51.106549] TEST-55-OOMD.sh[879]: + assert_eq signal oom-kill
[   51.107394] TEST-55-OOMD.sh[913]: + set +ex
[   51.108256] TEST-55-OOMD.sh[913]: FAIL: expected: 'oom-kill' actual: 'signal'
[FAILED] Failed to start TEST-55-OOMD.service.

To mitigate this, let's spawn a child process and move it to the
subcgroup to get killed instead of the main process, so systemd has more
time to react to the OOM notification and terminate the service with the
expected oom-kill result.

test/units/TEST-55-OOMD.sh

index 90d40069f7409d0547304b6a6878858a566ccaca..6010d67225d4475d6d45535afc99bc7aa4cab7af 100755 (executable)
@@ -301,6 +301,7 @@ testcase_reload() {
 testcase_kernel_oom() {
     cat >/tmp/script.sh <<"EOF"
 #!/usr/bin/env bash
+set -x
 choom --adjust '+1000' -- bash -c 'echo f >/proc/sysrq-trigger && exec sleep infinity'
 choom --adjust '+1000' -p $$
 echo f >/proc/sysrq-trigger
@@ -324,12 +325,22 @@ EOF
 
     cat >/tmp/script.sh <<"EOF"
 #!/usr/bin/env bash
+set -x
 echo '+memory' >/sys/fs/cgroup/system.slice/oom-kill.service/cgroup.subtree_control
 mkdir /sys/fs/cgroup/system.slice/oom-kill.service/sub
 echo 1 >/sys/fs/cgroup/system.slice/oom-kill.service/sub/memory.oom.group
-echo $$ >/sys/fs/cgroup/system.slice/oom-kill.service/sub/cgroup.procs
-choom --adjust '+1000' -p $$
-echo f >/proc/sysrq-trigger
+
+# Start a child process in the subcgroup that will trigger OOM and be killed but keep the main process
+# outside the subcgroup to avoid a race condition where the kernel SIGKILLs the main process before systemd
+# can process the OOM notification. With the main process still alive, systemd should have time to receive
+# the OOM event and enter the 'oom-kill' state before the service exits.
+(
+    echo $BASHPID >/sys/fs/cgroup/system.slice/oom-kill.service/sub/cgroup.procs
+    choom --adjust '+1000' -p $BASHPID
+    echo f >/proc/sysrq-trigger
+    exec sleep infinity
+) &
+wait $! || :
 exec sleep infinity
 EOF
     chmod +x /tmp/script.sh