diff --git a/script/set-cpu-frequency/customize.py b/script/set-cpu-frequency/customize.py new file mode 100644 index 000000000..b4f878d3d --- /dev/null +++ b/script/set-cpu-frequency/customize.py @@ -0,0 +1,59 @@ +from mlc import utils +import os +import subprocess + + +def preprocess(i): + + env = i['env'] + state = i['state'] + + freq = env.get('MLC_TARGET_FREQ', '').strip() + if freq != '': + try: + freq = parse_target_freq(freq) + except ValueError as e: + return {'return': 1, 'error': sys.stderr} + + os_info = i['os_info'] + + return {'return': 0} + + +def parse_target_freq(raw: str) -> int | None: + """ + Parse a freq string like '2300KHz', '2.3GHz', '2500M' or a plain integer + into an integer number of kHz. Returns None if the env var is empty. + """ + if not raw: + return None + + # match [.][unit], unit = k/M/G (case-insensitive), + # optional "Hz" + m = re.fullmatch(r"([0-9]+(?:\.[0-9]+)?)([KMGkmg])(?:[Hh][Zz])?", raw) + if m: + val, unit = m.group(1), m.group(2).lower() + val = float(val) + if unit == "g": + khz = int(val * 1_000_000) + elif unit == "m": + khz = int(val * 1_000) + else: # "k" + khz = int(val) + return khz + + # plain integer? treat as kHz + if raw.isdigit(): + return int(raw) + + raise ValueError(f"Invalid frequency format: '{raw}'") + + +def postprocess(i): + + env = i['env'] + state = i['state'] + + os_info = i['os_info'] + + return {'return': 0} diff --git a/script/set-cpu-frequency/meta.yaml b/script/set-cpu-frequency/meta.yaml new file mode 100644 index 000000000..85e6960dc --- /dev/null +++ b/script/set-cpu-frequency/meta.yaml @@ -0,0 +1,26 @@ +alias: set-cpu-frequency +automation_alias: script +automation_uid: 5b4e0237da074764 +category: MLC Sys Utils +deps: + - tags: detect,os + - tags: detect,sudo +new_env_keys: [] +new_state_keys: [] +post_deps: [] +posthook_deps: [] +prehook_deps: [] +input_mapping: + freq: MLC_TARGET_FREQ + frequency: MLC_TARGET_FREQ +tags: +- set +- cpu +- target +- freq +- frequency +tests: + run_inputs: + - freq: 1GHz + - freq: '' +uid: 1fe0500d7a2e4c6a diff --git a/script/set-cpu-frequency/run.sh b/script/set-cpu-frequency/run.sh new file mode 100644 index 000000000..57b22872a --- /dev/null +++ b/script/set-cpu-frequency/run.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +set -euo pipefail + +TARGET_FREQ="${MLC_TARGET_FREQ:-}" +DRIVER_FILE="/sys/devices/system/cpu/cpu0/cpufreq/scaling_driver" + +if [[ ! -r $DRIVER_FILE ]]; then + echo "Error: cannot read $DRIVER_FILE. Is cpufreq enabled?" >&2 + exit 2 +fi +DRIVER=$(<"$DRIVER_FILE") +echo "Detected cpufreq driver: $DRIVER" + +# Normalize AMD pstate variants +if [[ $DRIVER == amd-pstate* ]]; then + DRIVER_KEY="amd-pstate" +else + DRIVER_KEY="$DRIVER" +fi + + +case "$DRIVER_KEY" in + intel_pstate) + echo "→ intel_pstate: disabling turbo, setting performance governor" + echo 0 | ${MLC_SUDO} tee /sys/devices/system/cpu/intel_pstate/no_turbo >/dev/null + ${MLC_SUDO} cpupower frequency-set -g performance + ;; + + amd-pstate) + echo "→ amd_pstate: enabling boost, setting performance governor" + # boost file is global under cpufreq + if [[ -w /sys/devices/system/cpu/cpufreq/boost ]]; then + echo 1 | ${MLC_SUDO} tee /sys/devices/system/cpu/cpufreq/boost >/dev/null + fi + ${MLC_SUDO} cpupower frequency-set -g performance + echo "" + echo "Note: amd-pstate does _not_ support a userspace/fixed frequency mode." + echo "If you need a precise kHz, switch back to acpi-cpufreq in your kernel cmdline." + ;; + + acpi-cpufreq) + echo "→ acpi-cpufreq: switching to userspace governor + fixed freq" + if [[ -z "$TARGET_FREQ" ]]; then + TARGET_FREQ=$(< /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq) + echo " No target given; defaulting to min freq = ${TARGET_FREQ} kHz" + fi + ${MLC_SUDO} cpupower frequency-set -g userspace + ${MLC_SUDO} cpupower frequency-set -f "${TARGET_FREQ}" + ;; + + *) + echo "Unsupported driver: $DRIVER" >&2 + exit 3 + ;; +esac + +echo "" +echo "Resulting settings for CPU0:" +cpupower frequency-info | sed -n '1,5p' +