testsAndMisc/linux_configuration/scripts/single_use/fixes/lib/arch_cpu.sh
Krzysztof kuhy Rudnicki b4b6908d69
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Test arch_cpu.sh to 100% and take it off the allowlist (91 -> 90)
57/57 lines, measured through run_all.sh and confirmed a second time at
--min 100 so a shortfall would exit non-zero. check_shell_coverage.sh
passes with the exemption removed.

28 assertions over 14 cases: no cpufreq at all, every core already
tuned, the mixed case where only some cores lag, cpupower absent /
unconfigured / already configured, and all three I/O scheduler targets
(none for NVMe, mq-deadline for SATA SSD, bfq for spinning disks).

The sysfs reads and the two /etc write targets are now behind overrides
defaulting to the real paths, so the tests can present hardware this
host does not have -- a rotational disk -- without touching anything.

One assertion initially passed for the wrong reason: _t_hide keeps
$FAKE_BIN on PATH by design, so the harness's own cpupower stub still
satisfied has_cmd and the "not installed" branch never ran. Unstubbing
before hiding is now explicit, and the case fails if that is lost.

This also settles arch_sysctl.sh's diagnosis: it uses this same
harness, so the harness is not why it cannot be measured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 21:14:57 +02:00

129 lines
4.1 KiB
Bash
Executable File

#!/bin/bash
# CPU governor and IO scheduler tweaks.
#
# Sourced by optimize_arch_desktop.sh; split out to keep it under the 250-line cap.
# Sourced rather than run, so it inherits the caller's strict mode and
# the helper functions and variables defined above the source line.
# ===================================================================
# 1. CPU Governor → performance
# ===================================================================
tweak_cpu_governor() {
local gov_files
# CPU_SYSFS_ROOT and the two write targets below default to the real
# locations and are overridden only by the test harness, which runs
# un-jailed in ci_mirror.sh and in CI.
gov_files=$(find "${CPU_SYSFS_ROOT:-/sys/devices/system/cpu}" -maxdepth 3 -name scaling_governor 2>/dev/null || true)
if [[ -z $gov_files ]]; then
log_warn "No CPU governor sysfs files found — skipping."
return 0
fi
# Check current state
local all_performance=true
local f
for f in $gov_files; do
if [[ $(cat "$f") != "performance" ]]; then
all_performance=false
break
fi
done
if [[ $all_performance == "true" ]]; then
log_ok "All CPU cores already on 'performance' governor — skipping."
return 0
fi
for f in $gov_files; do
echo "performance" >"$f"
done
# Make it persistent via a sysctl-style drop-in using udev rule
local udev_rule="${UDEV_RULES_DIR:-/etc/udev/rules.d}/60-cpu-governor-performance.rules"
if [[ ! -f $udev_rule ]]; then
cat >"$udev_rule" <<'UDEVEOF'
# Set CPU governor to performance on all cores at boot
SUBSYSTEM=="module", DEVPATH=="*/cpu/*", ATTR{scaling_governor}=="*", ATTR{scaling_governor}="performance"
UDEVEOF
fi
# Also install cpupower hook as a more reliable persistence method
local cpupower_conf="${CPUPOWER_CONF_FILE:-/etc/default/cpupower}"
if has_cmd cpupower; then
if [[ ! -f $cpupower_conf ]] || ! grep -q "^governor='performance'" "$cpupower_conf" 2>/dev/null; then
mkdir -p "$(dirname "$cpupower_conf")"
cat >"$cpupower_conf" <<'CPUEOF'
# /etc/default/cpupower — managed by optimize_arch_desktop.sh
governor='performance'
CPUEOF
systemctl enable cpupower.service 2>/dev/null || true
fi
fi
return 0
}
# ===================================================================
# 2. I/O Scheduler per drive type
# ===================================================================
tweak_io_scheduler() {
local changed=false
local block_dev
local block_root="${BLOCK_SYSFS_ROOT:-/sys/block}"
for block_dev in "$block_root"/sd* "$block_root"/nvme* "$block_root"/vd*; do
[[ -d $block_dev ]] || continue
local sched_file="$block_dev/queue/scheduler"
[[ -f $sched_file ]] || continue
local dev_name
dev_name=$(basename "$block_dev")
local rotational
rotational=$(cat "$block_dev/queue/rotational" 2>/dev/null || echo 1)
local current
current=$(sed 's/.*\[\(.*\)\].*/\1/' "$sched_file" 2>/dev/null || true)
local target
if [[ $dev_name == nvme* ]]; then
target="none"
elif [[ $rotational -eq 0 ]]; then
target="mq-deadline"
else
target="bfq"
fi
if [[ $current == "$target" ]]; then
log_ok "$dev_name: already using '$target' scheduler."
continue
fi
echo "$target" >"$sched_file" 2>/dev/null || true
log_info "$dev_name: scheduler changed from '$current' to '$target'."
changed=true
done
# Persist via udev rule
local udev_rule="${UDEV_RULES_DIR:-/etc/udev/rules.d}/60-io-scheduler.rules"
if [[ ! -f $udev_rule ]]; then
cat >"$udev_rule" <<'IOEOF'
# NVMe: no scheduler (multi-queue hardware handles it)
ACTION=="add|change", KERNEL=="nvme[0-9]*", ATTR{queue/scheduler}="none"
# SATA SSD: mq-deadline (low latency)
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="0", ATTR{queue/scheduler}="mq-deadline"
# HDD: BFQ (fair bandwidth allocation)
ACTION=="add|change", KERNEL=="sd[a-z]", ATTR{queue/rotational}=="1", ATTR{queue/scheduler}="bfq"
IOEOF
fi
if [[ $changed == "false" ]]; then
log_ok "All I/O schedulers already optimal."
fi
return 0
}
# ===================================================================
# Apply all tweaks
# ===================================================================