Skip to content

Commit

Permalink
Make swap size configurable (home-assistant#3882)
Browse files Browse the repository at this point in the history
Allow configuration of the swap size via /etc/default/haos-swapfile file. By
setting the SWAPSIZE variable in this file, swapfile get recreated on the next
reboot to the defined size. Size can be either in bytes or with optional units
(B/K/M/G, accepting some variations but always interpreted as power of 10). The
size is then rounded to 4k block size. If no override is defined or the value
can't be parsed, it falls back to previously used 33% of system RAM.

Fixes home-assistant#968
  • Loading branch information
sairon authored Feb 19, 2025
1 parent dc7b693 commit d42e34f
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 15 deletions.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=Persistent /etc/default directory
Requires=mnt-overlay.mount
After=mnt-overlay.mount

[Mount]
What=/mnt/overlay/etc/default
Where=/etc/default
Type=None
Options=bind

[Install]
WantedBy=hassos-bind.target
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[Unit]
Description=HAOS swap
DefaultDependencies=no
Requires=mnt-data.mount
After=mnt-data.mount [email protected]
Requires=etc-default.mount mnt-data.mount
After=etc-default.mount mnt-data.mount [email protected]
Before=mnt-data-swapfile.swap

[Service]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[Unit]
Description=HAOS swap file
ConditionFileNotEmpty=/mnt/data/swapfile

[Swap]
What=/mnt/data/swapfile
Expand Down
61 changes: 48 additions & 13 deletions buildroot-external/rootfs-overlay/usr/libexec/haos-swapfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,59 @@
#!/bin/sh
set -e

swapfile="/mnt/data/swapfile"
size2kilobytes() {
bytes="$(echo "$1" | awk \
'BEGIN{IGNORECASE = 1}
function tobytes(n,b,p) {printf "%u\n", n*b^p/1024}
/[0-9]B?$/{tobytes($1, 1, 0); next};
/K(i?B)?$/{tobytes($1, 2, 10); next};
/M(i?B)?$/{tobytes($1, 2, 20); next};
/G(i?B)?$/{tobytes($1, 2, 30); next};
{print -1}')"
echo "$bytes"
}

if [ -f /etc/default/haos-swapfile ]; then
# shellcheck disable=SC1091
. /etc/default/haos-swapfile
fi
SWAPFILE="/mnt/data/swapfile"

# Swap size in kilobytes (as it's also what meminfo shows)
SWAPSIZE="$(size2kilobytes "${SWAPSIZE}")"

if [ -z "${SWAPSIZE}" ] || [ "${SWAPSIZE}" = "-1" ]; then
# Default to 33% of total memory
SWAPSIZE="$(awk '/MemTotal/{ print int($2 * 0.33) }' /proc/meminfo)"
echo "[INFO] Using default swapsize of 33% RAM (${SWAPSIZE} kB)"
fi

# Swap space in 4k blocks
swapsize="$(awk '/MemTotal/{ print int($2 * 0.33 / 4) }' /proc/meminfo)"
SWAPSIZE_BLOCKS=$((SWAPSIZE / 4))

if [ "${SWAPSIZE_BLOCKS}" -lt 10 ]; then
echo "[INFO] Requested swap size smaller than 40kB, disabling swap"

if [ ! -s "${swapfile}" ] || [ "$(stat "${swapfile}" -c '%s')" -lt $((swapsize * 4096)) ]; then
# Check free space (in 4k blocks)
if [ "$(stat -f /mnt/data -c '%f')" -lt "${swapsize}" ]; then
echo "[WARNING] Not enough space to allocate swapfile"
exit 1
fi
if [ -f "${SWAPFILE}" ]; then
echo "[INFO] Removing existing swapfile"
rm -f "${SWAPFILE}"
fi

echo "[INFO] Creating swapfile of size $((swapsize *4))k"
umask 0077
dd if=/dev/zero of="${swapfile}" bs=4k count="${swapsize}"
exit 0
fi

if ! swaplabel "${swapfile}" > /dev/null 2>&1; then
/usr/lib/systemd/systemd-makefs swap "${swapfile}"
if [ ! -s "${SWAPFILE}" ] || [ "$(stat "${SWAPFILE}" -c '%s')" -ne $((SWAPSIZE_BLOCKS * 4096)) ]; then
# Check free space (in 4k blocks)
if [ "$(stat -f /mnt/data -c '%f')" -lt "${SWAPSIZE_BLOCKS}" ]; then
echo "[ERROR] Not enough space to allocate swapfile"
exit 1
fi

echo "[INFO] Creating swapfile of size ${SWAPSIZE} kB (rounded to ${SWAPSIZE_BLOCKS} blocks)"
umask 0077
dd if=/dev/zero of="${SWAPFILE}" bs=4k count="${SWAPSIZE_BLOCKS}"
fi

if ! swaplabel "${SWAPFILE}" > /dev/null 2>&1; then
/usr/lib/systemd/systemd-makefs swap "${SWAPFILE}"
fi
25 changes: 25 additions & 0 deletions tests/smoke_test/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ def test_systemctl_check_no_failed(shell):
assert "0 loaded units listed." in output, f"Some units failed:\n{"\n".join(output)}"


@pytest.mark.dependency(depends=["test_init"])
def test_custom_swap_size(shell, target):
output = shell.run_check("stat -c '%s' /mnt/data/swapfile")
# set new swap size to half of the previous size - round to 4k blocks
new_swap_size = (int(output[0]) // 2 // 4096) * 4096
shell.run_check(f"echo 'SWAPSIZE={new_swap_size/1024/1024}M' > /etc/default/haos-swapfile; reboot")
# reactivate ShellDriver to handle login again
target.deactivate(shell)
target.activate(shell)
output = shell.run_check("stat -c '%s' /mnt/data/swapfile")
assert int(output[0]) == new_swap_size, f"Incorrect swap size {new_swap_size}B: {output}"


@pytest.mark.dependency(depends=["test_custom_swap_size"])
def test_no_swap(shell, target):
output = shell.run_check("echo 'SWAPSIZE=0' > /etc/default/haos-swapfile; reboot")
# reactivate ShellDriver to handle login again
target.deactivate(shell)
target.activate(shell)
output = shell.run_check("systemctl --no-pager -l list-units --state=failed")
assert "0 loaded units listed." in output, f"Some units failed:\n{"\n".join(output)}"
swapon = shell.run_check("swapon --show")
assert swapon == [], f"Swapfile still exists: {swapon}"


@pytest.mark.dependency(depends=["test_init"])
def test_kernel_not_tainted(shell):
"""Check if the kernel is not tainted - do it at the end of the
Expand Down

0 comments on commit d42e34f

Please sign in to comment.