Let systemd-oomd kill a runaway app instead of losing the session (#6445)
Nothing stood between "memory is tight" and "processes die at random": the kernel OOM killer only fires after an allocation has already failed, so a machine under pressure thrashes in reclaim until something breaks somewhere fatal — seen in practice as Hyprland taking SIGBUS mid-memcpy from a client shm pool that memory pressure was tearing down. Enable systemd-oomd, which keys on PSI stall time rather than free pages, and mark app.slice — and only app.slice — as its kill candidate. Hyprland runs in session.slice, so the compositor is structurally ineligible as a victim: oomd takes the app that caused the pressure and the session survives. Thresholds (50% stall over 20s, Fedora's desktop defaults) live in an oomd.conf.d drop-in; candidacy ships as a vendor drop-in under /usr/lib/systemd/user so existing users get it on package upgrade with no per-user seeding. New installs enable the daemon from enable-services.sh; a migration covers existing ones, restarting an already-running oomd so it doesn't keep stale thresholds until reboot. Deliberately not earlyoom: it triggers only when free RAM and free swap are both under threshold, so Omarchy's large, mostly-idle swapfiles keep it asleep through exactly this failure. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
5844e02d2e
commit
1ea910f662
@@ -76,3 +76,34 @@ grep -F 'omarchy-fcitx5.service' "$first_run_units" >/dev/null ||
|
||||
grep -F 'fcitx5' "$ROOT/default/hypr/autostart.lua" >/dev/null &&
|
||||
fail "fcitx5 is autostarted from Hyprland; an unsupervised launch dies silently and takes every compose sequence with it"
|
||||
pass "fcitx5 runs supervised, so a lost input method comes back instead of killing XCompose until logout"
|
||||
|
||||
oomd_slice="$ROOT/default/systemd/user/app.slice.d/10-oomd.conf"
|
||||
grep -Fx 'ManagedOOMMemoryPressure=kill' "$oomd_slice" >/dev/null ||
|
||||
fail "nothing is a kill candidate, so systemd-oomd watches the machine thrash and never acts"
|
||||
grep -Fx 'ManagedOOMSwap=kill' "$oomd_slice" >/dev/null ||
|
||||
fail "no swap backstop for the slower shape of the same failure"
|
||||
|
||||
# Hyprland lives in session.slice/wayland-wm@hyprland.desktop.service. Marking
|
||||
# any ancestor of that as a kill candidate puts the compositor back in the
|
||||
# victim pool, which is the crash this whole thing exists to prevent.
|
||||
candidates=$(grep -rlE '^ManagedOOM(MemoryPressure|Swap)=kill' "$ROOT/default/systemd" "$ROOT/etc/systemd" 2>/dev/null || true)
|
||||
[[ $candidates == "$oomd_slice" ]] ||
|
||||
fail "systemd-oomd kill candidacy is set outside app.slice, which can select the compositor: $candidates"
|
||||
pass "only user app scopes are systemd-oomd kill candidates"
|
||||
|
||||
oomd_conf="$ROOT/etc/systemd/oomd.conf.d/10-omarchy.conf"
|
||||
grep -Fx 'DefaultMemoryPressureLimit=50%' "$oomd_conf" >/dev/null ||
|
||||
fail "no pressure limit; the 60% default rides thrashing longer than a desktop stays usable"
|
||||
grep -Fx 'DefaultMemoryPressureDurationSec=20s' "$oomd_conf" >/dev/null ||
|
||||
fail "no pressure duration set for the tightened limit"
|
||||
pass "systemd-oomd acts on sustained memory stall"
|
||||
|
||||
grep -Fx 'systemctl enable systemd-oomd.service' "$ROOT/install/config/enable-services.sh" >/dev/null ||
|
||||
fail "new installs ship the oomd drop-ins with the daemon that reads them disabled"
|
||||
|
||||
oomd_migration=$(grep -rl 'systemd-oomd.service' "$ROOT/migrations" | head -n 1 || true)
|
||||
[[ -n $oomd_migration ]] ||
|
||||
fail "existing installs never enable systemd-oomd; enable-services.sh only runs at install time"
|
||||
grep -F 'systemctl --user daemon-reload' "$oomd_migration" >/dev/null ||
|
||||
fail "migration leaves the user manager unaware of app.slice candidacy until the next login"
|
||||
pass "existing installs enable systemd-oomd and report app.slice without a relogin"
|
||||
|
||||
Reference in New Issue
Block a user