Size zram the way Fedora does
zram-size = min(ram, 8192) is Fedora's default since F34, where zram-fraction=1.0 with max-zram-size=8192 has run on desktops for five years. The previous min(ram / 2, 8192) halved that on every machine below 16G with nothing behind the fraction; at 16G and above the two already agree. A changed size only reaches a running device on the next boot, so the migration resizes an empty device now and asks for a reboot otherwise. It asks the generator what the config evaluates to rather than repeating the expression here, and does neither when the device already matches. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f01acc1de9
commit
187c2ecf7f
@@ -1,10 +1,17 @@
|
|||||||
# Compressed swap in RAM. zstd averages around 3:1, so the worst case for a
|
# Compressed swap in RAM, sized the way Fedora has shipped it since F34: all of
|
||||||
# full device is roughly a third of the size below.
|
# memory, capped at 8G. Fedora weighed the worry that incompressible pages would
|
||||||
|
# tie up RAM against five years of desktops reporting no trouble from it, which
|
||||||
|
# is more field evidence than we could gather on our own.
|
||||||
#
|
#
|
||||||
# The generator's own default caps this at 4G, which sends machines with a lot
|
# The cap is what bounds the downside. A browser-heavy desktop can drive zstd
|
||||||
# of memory to the disk swapfile far earlier than they need to go.
|
# down near 1.1:1, where the device backs far less than it advertises, and 8G
|
||||||
|
# keeps that bounded on any machine large enough to notice. The disk swapfile
|
||||||
|
# sits behind it at pri=0 for everything past that.
|
||||||
|
#
|
||||||
|
# Fedora spells this zram-fraction = 1.0 with max-zram-size = 8192; both keys
|
||||||
|
# are deprecated in favour of zram-size, which computes the same numbers.
|
||||||
[zram0]
|
[zram0]
|
||||||
zram-size = min(ram / 2, 8192)
|
zram-size = min(ram, 8192)
|
||||||
compression-algorithm = zstd
|
compression-algorithm = zstd
|
||||||
|
|
||||||
# Above the pri=0 that omarchy-hibernation-setup gives the disk swapfile, so
|
# Above the pri=0 that omarchy-hibernation-setup gives the disk swapfile, so
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
echo "Resize zram to match the shipped config"
|
||||||
|
|
||||||
|
# 90-omarchy.conf changed the device size. The migration that first applied the
|
||||||
|
# zram tuning is one-shot, so machines that already ran it have the new file on
|
||||||
|
# disk and the old device still running; nothing would pick the size up until
|
||||||
|
# something else rebooted them.
|
||||||
|
|
||||||
|
zram_disksize=${OMARCHY_ZRAM_DISKSIZE:-/sys/block/zram0/disksize}
|
||||||
|
swaps=${OMARCHY_SWAPS:-/proc/swaps}
|
||||||
|
|
||||||
|
# The size is an expression, and the generator is the only thing that evaluates
|
||||||
|
# it. Ask it what the shipped config comes to rather than repeating the
|
||||||
|
# arithmetic here, where the two would drift apart on the next tuning change.
|
||||||
|
units=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$units"' EXIT
|
||||||
|
desired_mb=$(/usr/lib/systemd/system-generators/zram-generator "$units" 2>&1 |
|
||||||
|
grep -oP '/dev/zram0 with \K[0-9]+') || true
|
||||||
|
|
||||||
|
actual_bytes=$(cat "$zram_disksize" 2>/dev/null) || actual_bytes=0
|
||||||
|
|
||||||
|
# A device already at the right size needs neither a restart nor a reboot,
|
||||||
|
# whether an earlier boot picked the config up or another user got here first.
|
||||||
|
if [[ -n $desired_mb && $actual_bytes == $((desired_mb * 1024 * 1024)) ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if sudo systemctl daemon-reload; then
|
||||||
|
# Resizing swaps the device off first, which faults every stored page back
|
||||||
|
# into memory. That's only cheap while it's empty, so a device under
|
||||||
|
# pressure keeps its old size until the next boot.
|
||||||
|
zram_used=$(awk '$1 == "/dev/zram0" {print $4}' "$swaps")
|
||||||
|
|
||||||
|
if [[ ${zram_used:-0} == 0 ]] && sudo systemctl restart dev-zram0.swap; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
omarchy-state set reboot-required
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
source "$(dirname "${BASH_SOURCE[0]}")/base-test.sh"
|
||||||
|
|
||||||
|
require_command /usr/lib/systemd/system-generators/zram-generator
|
||||||
|
|
||||||
|
migration=$(grep -rl 'Resize zram to match the shipped config' "$ROOT/migrations" | head -n 1 || true)
|
||||||
|
[[ -n $migration ]] || fail "zram resize migration exists"
|
||||||
|
|
||||||
|
TMPDIR=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$TMPDIR"' EXIT
|
||||||
|
|
||||||
|
# The migration shells out to sudo, systemctl and omarchy-state. Stub all three
|
||||||
|
# so the test never touches the real system, and record what each run did.
|
||||||
|
stub_bin="$TMPDIR/bin"
|
||||||
|
mkdir -p "$stub_bin"
|
||||||
|
|
||||||
|
cat >"$stub_bin/sudo" <<'STUB'
|
||||||
|
#!/bin/bash
|
||||||
|
exec "$@"
|
||||||
|
STUB
|
||||||
|
|
||||||
|
cat >"$stub_bin/systemctl" <<'STUB'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "systemctl $*" >>"$ACTIONS"
|
||||||
|
[[ ${SYSTEMCTL_FAIL:-0} == 1 ]] && exit 1
|
||||||
|
exit 0
|
||||||
|
STUB
|
||||||
|
|
||||||
|
cat >"$stub_bin/omarchy-state" <<'STUB'
|
||||||
|
#!/bin/bash
|
||||||
|
echo "omarchy-state $*" >>"$ACTIONS"
|
||||||
|
STUB
|
||||||
|
|
||||||
|
chmod +x "$stub_bin/sudo" "$stub_bin/systemctl" "$stub_bin/omarchy-state"
|
||||||
|
|
||||||
|
# ZRAM_GENERATOR_ROOT redirects both the config search and /proc/meminfo, so the
|
||||||
|
# migration's own generator call resolves against this fixture instead of the
|
||||||
|
# host. 16G of RAM against the shipped config is a 8192MB device.
|
||||||
|
gen_root="$TMPDIR/genroot"
|
||||||
|
mkdir -p "$gen_root/usr/lib/systemd/zram-generator.conf.d" "$gen_root/proc"
|
||||||
|
cp "$ROOT/default/systemd/zram-generator.conf.d/90-omarchy.conf" \
|
||||||
|
"$gen_root/usr/lib/systemd/zram-generator.conf.d/"
|
||||||
|
printf 'MemTotal: %d kB\n' $((16 * 1024 * 1024)) >"$gen_root/proc/meminfo"
|
||||||
|
|
||||||
|
desired_bytes=$((8192 * 1024 * 1024))
|
||||||
|
|
||||||
|
# omarchy-migrate runs each migration with `bash -euo pipefail` and stops the
|
||||||
|
# whole chain on a non-zero exit, so match that invocation exactly.
|
||||||
|
run_migration() {
|
||||||
|
local disksize="$1" used="$2" fail_reload="${3:-0}"
|
||||||
|
|
||||||
|
printf '%s' "$disksize" >"$TMPDIR/disksize"
|
||||||
|
printf 'Filename\tType\tSize\tUsed\tPriority\n' >"$TMPDIR/swaps"
|
||||||
|
[[ -n $used ]] &&
|
||||||
|
printf '/dev/zram0 partition 8388604 %s 100\n' "$used" >>"$TMPDIR/swaps"
|
||||||
|
|
||||||
|
: >"$TMPDIR/actions"
|
||||||
|
|
||||||
|
PATH="$stub_bin:$PATH" \
|
||||||
|
ACTIONS="$TMPDIR/actions" \
|
||||||
|
SYSTEMCTL_FAIL="$fail_reload" \
|
||||||
|
ZRAM_GENERATOR_ROOT="$gen_root" \
|
||||||
|
OMARCHY_ZRAM_DISKSIZE="$TMPDIR/disksize" \
|
||||||
|
OMARCHY_SWAPS="$TMPDIR/swaps" \
|
||||||
|
bash -euo pipefail "$migration" >/dev/null ||
|
||||||
|
fail "migration exits clean (disksize=$disksize used=$used reload_fail=$fail_reload)"
|
||||||
|
}
|
||||||
|
|
||||||
|
did() { grep -qF "$1" "$TMPDIR/actions"; }
|
||||||
|
|
||||||
|
# Already the size the config asks for: the device is correct however it got
|
||||||
|
# there, so the migration must not restart it or ask for a reboot.
|
||||||
|
run_migration "$desired_bytes" 4193612
|
||||||
|
[[ -s $TMPDIR/actions ]] && fail "correctly sized device is left alone" \
|
||||||
|
"expected no privileged calls, got: $(cat "$TMPDIR/actions")"
|
||||||
|
pass "correctly sized device is left alone"
|
||||||
|
pass "correctly sized device asks for no reboot"
|
||||||
|
|
||||||
|
# Right size but still in use, and the reload never even happens.
|
||||||
|
run_migration "$desired_bytes" 0
|
||||||
|
did "systemctl restart" && fail "correctly sized empty device is left alone"
|
||||||
|
pass "correctly sized empty device is left alone"
|
||||||
|
|
||||||
|
# Wrong size and empty: safe to resize now.
|
||||||
|
run_migration $((4096 * 1024 * 1024)) 0
|
||||||
|
did "systemctl restart dev-zram0.swap" || fail "empty device is restarted"
|
||||||
|
did "omarchy-state set reboot-required" && fail "empty device asks for no reboot"
|
||||||
|
pass "empty device is restarted"
|
||||||
|
pass "empty device asks for no reboot"
|
||||||
|
|
||||||
|
# Wrong size with pages stored: resizing would fault them all back in, so defer.
|
||||||
|
run_migration $((4096 * 1024 * 1024)) 4193612
|
||||||
|
did "systemctl restart" && fail "device in use is not restarted"
|
||||||
|
did "omarchy-state set reboot-required" || fail "device in use asks for a reboot"
|
||||||
|
pass "device in use is not restarted"
|
||||||
|
pass "device in use asks for a reboot"
|
||||||
|
|
||||||
|
# No zram device at all reads as empty, and the restart brings it up.
|
||||||
|
run_migration "" ""
|
||||||
|
did "systemctl restart dev-zram0.swap" || fail "absent device is created"
|
||||||
|
pass "absent device is created"
|
||||||
|
|
||||||
|
# A failed daemon-reload must fall back to asking for a reboot.
|
||||||
|
run_migration $((4096 * 1024 * 1024)) 0 1
|
||||||
|
did "systemctl restart" && fail "failed reload does not restart"
|
||||||
|
did "omarchy-state set reboot-required" || fail "failed reload asks for a reboot"
|
||||||
|
pass "failed reload does not restart"
|
||||||
|
pass "failed reload asks for a reboot"
|
||||||
Reference in New Issue
Block a user