#!/bin/sh # Transparent huge pages as the mesh sets them on this machine (module kernel, novox/hq issue 746). # Written by the mesh from this machine's setting transparent-huge-pages and run by # mesh-kernel-huge-pages.service: `apply` at boot and whenever the setting changes, `restore` when the # unit stops (the module unassigned, or the machine shutting down). Replaced on every push; change the # setting, never this file. A change of the setting reloads the unit, which runs `apply` again: the mode # moves straight to the new value and never passes back through the one the machine was found with. # # huge-pages apply|restore [ []] # # The two directories are arguments only so the module's tests can run this against a stand-in tree. set -eu want='${config:transparent-huge-pages}' sys=/sys/kernel/mm/transparent_hugepage state='${dir:state}' [ $# -ge 2 ] && sys=$2 [ $# -ge 3 ] && state=$3 case "$want" in always | madvise | never) ;; *) echo "the setting transparent-huge-pages is '$want'; the kernel takes always, madvise or never" >&2 exit 2 ;; esac [ -f "$sys/enabled" ] || { echo "this kernel has no $sys/enabled: it was built without transparent huge pages, so there is nothing to set" >&2 exit 1 } # The mode in force is the word the kernel brackets: "always [madvise] never". A file holding one bare # word (the stand-in tree of the tests) is read as that word. mode() { sed -n -e 's/.*\[\([a-z]*\)\].*/\1/p' -e t -e 's/^ *\([a-z]*\) *$/\1/p' "$sys/enabled"; } [ $# -ge 1 ] || set -- usage case "$1" in apply) now=$(mode) # What the machine had before the mesh first set it, kept once, so `restore` gives it back. [ -s "$state/found" ] || printf '%s\n' "$now" >"$state/found" if [ "$now" = "$want" ]; then echo "transparent huge pages: $want, as set" exit 0 fi printf '%s\n' "$want" >"$sys/enabled" got=$(mode) [ "$got" = "$want" ] || { echo "wrote $want to $sys/enabled and the kernel says $got" >&2 exit 1 } echo "transparent huge pages: $now -> $want" # A file folio already in the page cache keeps its size: the active journal files, mapped by journald # and by every reader, stay in 2 MiB folios until they are archived (issue 746). Rotating starts new # files that are mapped under the new mode. A failed rotation is said and does not fail the setting. # At boot nothing is in huge folios yet, and the persistent journal opens only after this unit: no rotation # while the service manager is still bringing the machine up. booting=$(systemctl is-system-running 2>/dev/null || true) case "$booting" in initializing | starting) echo "the machine is still starting: nothing to rotate yet" ;; *) if [ "$want" != always ] && command -v journalctl >/dev/null 2>&1; then journalctl --rotate || echo "the journal could not be rotated: its active files keep their huge pages until they are archived" >&2 fi ;; esac ;; restore) found=$(cat "$state/found" 2>/dev/null || true) case "$found" in always | madvise | never) printf '%s\n' "$found" >"$sys/enabled" echo "transparent huge pages: back to $found, as the machine had them" ;; *) echo "transparent huge pages: nothing recorded as found, left at $(mode)" ;; esac # Unassigned or shut down: nothing of the module is kept; the next start records what it finds again. rm -f "$state/found" ;; *) echo "usage: huge-pages apply|restore [ []]" >&2 exit 2 ;; esac