0

DISTRO: Fedora 41 KDE

GOAL: Im trying to automatically set some cpu parameters (per-CPU max frequency, governors, energy policy, etc.) during my boot process. I wrote a script to do this (it lives at /usr/local/bin/cpu-tune.sh)...I just need this to get run automatically.

WHY: My i9-7940x will happily draw 300 W all day if it has sufficient work to do. These tweaks allow me to selectively slow down the hottest cores so they run cooler. I lose maybe 1% max performance, in in exchange and my hottest CPU core temps drop by 10 C. (and i no longer fail any of the mprime tests)

CUSTOM BOOT SEQUENCE: My system bootup is unlike most Fedora systems. I use Unified Kernel Images (UKIs) to boot directly from the UEFI (no grub/systemd-boot). These images are signed with my personal signing key, since i use secure boot with all keys other than my own removed. The UKI then asks for my password, unlocks a LUKS partition, imports the root ZFS pool from the device-mapper block device (I use ZFS on root) and mounts the various ZFS datasets that make up my root filesystem.

WHAT IVE TRIED: a bunch of stuff.

first I tried a simple systemd service that was more-or-less:

[Unit]
Description=Force CPU tuning udev re-trigger
After=systemd-udevd.service systemd-udev-trigger.service

[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/usr/bin/bash /usr/local/bin/cpu-tune.sh

[Install]
WantedBy=multi-user.target

This didnt work, even if I manually started the service with systemctl.

NOTE: running /usr/bin/bash /usr/local/bin/tune-cpu.sh manually works...but it doesnt in the systemd service.

so i tried adding some extra capabilities

User=root 

# shared keyring 
KeyringMode=shared 

# Required privileges 
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_SYS_RAWIO CAP_DAC_OVERRIDE 
AmbientCapabilities=CAP_SYS_ADMIN CAP_SYS_RAWIO CAP_DAC_OVERRIDE 

# Disable systemd protections that block /sys writes 
ProtectSystem=no
ProtectKernelTunables=no 
ProtectKernelModules=no 
ProtectControlGroups=no

and...no change. still doesnt work. so i figured id try udev instead. added a custom rule

ACTION=="add|change", SUBSYSTEM=="cpu", KERNEL=="cpu[0-9]*", RUN+="/usr/bin/bash /usr/local/bin/cpu-tune.sh %k"

and made cpu-tune.sh:

/usr/local/bin/cpu-tune.sh is:
#!/usr/bin/bash
# cpu-tune.sh - invoked by udev with %k (e.g. cpu0)
# Keep this tiny and fast: udev wants short-running helpers.

nn="${1##*[^0-9]}"

# small helper to write safely (retry a few times)
safe_write() {
  local file="$1" 
  local value="$2"
  local i=0
  while :; do
    if [ -w "${file}" ] || [ ! -e "${file}" ] && [ -w "$(dirname "$file")" ]; then
      printf '%s' "$value" > "$file" && return 0
    fi
    i=$((i+1))
    if [ $i -ge 5 ]; then
      printf '%s: failed writing %s -> %s\n' "$(date -Iseconds)" "$file" "$value" >> /var/log/cpu-tune.log
      return 1
    fi
    sleep 0.05
  done
}

case "$nn" in
  3|5|17|19)
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/energy_performance_preference" "balance_performance"
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/scaling_governor" "powersave"
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/scaling_max_freq" "3900000"
    safe_write "/sys/devices/system/cpu/cpu${nn}/power/energy_perf_bias" "15"
    ;;
  4|8|9|11|18|22|23|25)
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/energy_performance_preference" "balance_performance"
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/scaling_governor" "powersave"
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/scaling_max_freq" "4000000"
    safe_write "/sys/devices/system/cpu/cpu${nn}/power/energy_perf_bias" "15"
    ;;
  1|15|13|27)
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/energy_performance_preference" "balance_performance"
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/scaling_governor" "powersave"
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/scaling_max_freq" "4100000"
    safe_write "/sys/devices/system/cpu/cpu${nn}/power/energy_perf_bias" "15"
    ;;
  *)
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/energy_performance_preference" "performance"
    safe_write "/sys/devices/system/cpu/cpu${nn}/cpufreq/scaling_governor" "performance"
    safe_write "/sys/devices/system/cpu/cpu${nn}/power/energy_perf_bias" "0"
    ;;
esac

# also try enabling hwp dynamic boost once per invocation (harmless if not present)
safe_write "/sys/devices/system/cpu/intel_pstate/hwp_dynamic_boost" "1" || true

This one i can trigger with udevadm trigger --subsystem-match=cpu. But does it trigger on boot? no, of course not. So i figure ill have systemd run the udevadm command for me. i change the systemd service above so that

ExecStart=/bin/udevadm trigger --subsystem-match=cpu

And it works! well, if i manually start the service at least. But does that service run during boot? no, no it doesnt.

so, finally, i recreate my UKI so that the udev rules and the xcpu-tune.sh script are present. And.....still doesnt work.

At this point im sorta out of ideas....anyone know what im doing wrong?

New contributor
Anthony B is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
  • Do you have any SELinux AVC denials in your dmesg? The ambient capabilities are redundant as root always has them, but SELinux is a MAC so it has priority. Commented 15 hours ago

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.