Dynamic Performance Governor Management Script with Systemd

Slug: dynamic-performance-governor-management-script-with-systemd

11424 characters 1438 words

This guide presents a comprehensive, single approach to managing CPU, GPU, NPU, and DMC frequency governors on Linux systems, particularly for devices like the RK3588, by setting them to “performance” mode. This method emphasizes robustness, dynamic discovery of hardware, state persistence for reversibility, and proper integration with systemd. It combines the best practices from various methods into one refined solution.

While the context often mentions RK3588, the script and systemd service are designed to be generally applicable to Linux systems, though specific sysfs paths for governors can vary if not covered by the general patterns.

This solution consists of a powerful Bash script that handles the logic of discovering, setting, and restoring governors, and a systemd service unit to manage this script at boot and allow system-level control.

1. The Bash Script: /usr/local/bin/performance_governors.sh

This script is the core of the solution. Its key features include:

  • Root Privilege Check: Ensures it’s run with necessary permissions.
  • Robust Scripting: Uses set -euo pipefail for strict error handling and IFS=$’\n\t’ for safer processing of paths.
  • Variable Protection: Uses readonly for global configuration variables (STATE_DIR, STATE_FILE, GOV_PATTERNS) to prevent accidental modification within the script, enhancing robustness.
  • Dynamic Governor Discovery: Automatically finds relevant cpufreq (CPU) and devfreq (GPU, NPU, DMC, etc.) governor files using general patterns, making it adaptable to different hardware configurations.
  • State Management: Before setting governors to “performance,” it saves the current (default) governor for each device to a state file (/var/lib/performance_governors/default_gov.txt). This allows for a clean restoration of previous settings.
  • Comprehensive Actions:
    • start: Saves current governors and sets all discovered ones to “performance.”
    • stop: Restores the saved governors from the state file.
    • restart: Performs a stop then start.
    • status: Displays the current governor and the saved (default) governor for each discovered path.
  • Systemd-Aware Logging: Uses systemd-cat for logging if available, providing integration with the system journal; otherwise, falls back to the standard logger utility.
#!/usr/bin/env bash # —————————————————————————— # performance_governors.sh # Unified script to manage CPU/GPU/NPU/DMC frequency governors. # Targets devices like RK3588 but designed for general Linux applicability. # - Requires root (CAP_SYS_ADMIN) # - Depends on ‘util-linux’ (for logger) and systemd (for systemd-cat) # —————————————————————————— set -euo pipefail IFS=$’\n\t# Set Internal Field Separator to newline and tab for safer loops. ### Verify running as root if [$(id -u)-ne 0 ]; then echo “ERROR: Must be run as root.” >&2 exit 1 fi # Location to save/restore default governors readonly STATE_DIR=“/var/lib/performance_governors” readonly STATE_FILE=$STATE_DIR/default_gov.txt” # General patterns covering cpufreq (CPU) and devfreq (other devices) governors readonly GOV_PATTERNS=( “/sys/devices/system/cpu/cpufreq/policy*/scaling_governor” # For CPU cores “/sys/class/devfreq/*/governor” # For GPU, NPU, DMC, etc. ) # Log function: prefers systemd-cat for journal integration, else falls back to logger. log() { local level=$1; shift local msg=$*if command -v systemd-cat &>/dev/null; then # Pipe to systemd-cat to log with specified level and tag printf ‘%s\n’ “$msg” | systemd-cat -t performance_governors -p$levelelse # Fallback to logger if systemd-cat is not available logger -t performance_governors -p “user.$level” — “$msgfi } # Discover all existing governor file paths based on GOV_PATTERNS. discover_paths() { for patt in${GOV_PATTERNS[@]}; do # $patt is intentionally unquoted to allow shell pathname expansion (globbing). # The shell first performs word splitting on $patt (if it contained IFS characters; # current IFS is newline/tab). Then, pathname expansion (globbing, e.g., ‘*’) # is attempted on each resulting word. For the defined GOV_PATTERNS, which are # single “words” without internal IFS characters, this robustly expands the globs. for f in $patt; do # Check if the glob expansion resulted in an actual existing file [ -f$f] && printf ‘%s\n’ “$fdone done } # Action: Save current governors and set all to ‘performance’. cmd_start() { log info “START: Saving default governors and forcing ‘performance’ mode.” mkdir -p$STATE_DIR# Ensure state directory exists : >$STATE_FILE# Truncate/create state file # Read each discovered governor path while IFS= read -r path; do current_governor=$(<“$path) # Read current governor printf ‘%s\t%s\n’ “$path” “$current_governor>>$STATE_FILE# Save path and current governor # Attempt to set to ‘performance’ if echo performance >$path; then log info “Successfully set ‘performance’ for: $pathelse log err “FAILED to set ‘performance’ for: $pathfi done < <(discover_paths) # Process substitution feeds paths from discover_paths log info “START command complete.” } # Action: Restore governors to their saved default states. cmd_stop() { log info “STOP: Restoring saved default governors.” if [ ! -r$STATE_FILE]; then log warning “State file ($STATE_FILE) not found or not readable. Skipping restore.” return 1 # Indicate issue if state file is missing fi # Read path and old governor from state file while IFS=$’\tread -r path old_governor; do if [ -f$path]; then # Check if path still exists if echo$old_governor>$path; then log info “Restored ‘$old_governor’ to: $pathelse log err “FAILED to restore ‘$old_governor’ to: $pathfi else log warning “Path no longer exists, cannot restore for: $pathfi done <“$STATE_FILE” log info “STOP command complete.” } # Action: Display current and saved governors. cmd_status() { echo “Governor Status (Current Governor → Saved Default Governor):” declare -A saved_governors # Associative array to hold saved states # Populate saved_governors from state file if it exists if [ -r$STATE_FILE]; then while IFS=$’\tread -r path old_governor; do saved_governors[“$path”]=$old_governordone <“$STATE_FILEfi # Display status for each discovered governor path local found_any=0 while IFS= read -r path; do found_any=1 current_governor=$(<“$path) default_governor=${saved_governors[$path]:-<not_saved>}# Use <not_saved> if not in state file printf ‘%-65s : %-15s → %s\n’ “$path” “$current_governor” “$default_governordone < <(discover_paths) if [$found_any-eq 0 ]; then echo “No governor paths found.” fi } # Display usage instructions. usage() { cat <<EOF Usage: $0 {start|stop|restart|status} start Saves current governor settings and sets all to ‘performance’. stop Restores previously saved governor settings. restart Executes ‘stop’ then ‘start’. status Displays current vs. saved governor settings for all discovered paths. EOF exit 1 } # Main command dispatcher. if [ $# -eq 0 ]; then # Show usage if no arguments usage fi case$1in start) cmd_start ;; stop) cmd_stop ;; restart) cmd_stop; cmd_start ;; # Simple restart: stop then start status) cmd_status ;; *) usage ;; # Show usage for unknown commands esac

2. The Systemd Service Unit: /etc/systemd/system/performance_governors.service

This service file allows systemd to manage the script.

  • Type=oneshot: Indicates the script runs once and exits.
  • RemainAfterExit=yes: Tells systemd to consider the service “active” even after the ExecStart process finishes, as the script’s effects (changed governors) persist.
  • ExecStart, ExecStop, ExecReload: Map directly to the script’s start, stop, and restart actions.
  • Dependencies (After, Wants): Ensures the service starts at an appropriate time during boot.
  • Restart=no: The Restart=no directive ensures that if the script fails during startup (e.g., cannot set a governor), systemd will not attempt to restart it automatically. This is generally preferred for configuration services where a failure might indicate a deeper issue requiring manual investigation rather than repeated, potentially problematic, attempts.
[Unit] Description=Performance Governors Management (CPU/GPU/NPU/DMC) Documentation=man:performance_governors.sh # Assuming script could have a man page After=multi-user.target Wants=network-online.target # Optional: if any governor settings depend on network state [Service] Type=oneshot ExecStart=/usr/local/bin/performance_governors.sh start ExecStop=/usr/local/bin/performance_governors.sh stop ExecReload=/usr/local/bin/performance_governors.sh restart RemainAfterExit=yes # On failure, log and stay failed. Use ‘systemctl reset-failed performance_governors.service’ to clear. Restart=no [Install] WantedBy=multi-user.target

3. Installation and Management Instructions

  1. Install Prerequisites (if not already present): The script uses standard utilities. util-linux (for logger) and systemd (for systemd-cat and service management) are typically core components of modern Linux distributions.
    # On Debian/Ubuntu based systems, these are usually pre-installed. # sudo apt update # sudo apt install -y util-linux systemd
  2. Save the Bash Script: Copy the script code above and save it as performance_governors.sh in a temporary location.

  3. Install the Bash Script: Move it to /usr/local/bin/ and make it executable:
    sudo cp performance_governors.sh /usr/local/bin/performance_governors.sh sudo chmod 755 /usr/local/bin/performance_governors.sh
  4. Save the Systemd Service Unit: Copy the systemd unit content above and save it as performance_governors.service in a temporary location.

  5. Install the Systemd Service Unit: Move it to /etc/systemd/system/:
    sudo cp performance_governors.service /etc/systemd/system/performance_governors.service
  6. Reload Systemd, Enable and Start the Service:
    • daemon-reload: Makes systemd aware of the new service file.
    • enable: Ensures the service starts automatically on boot.
    • start: Starts the service immediately for the current session.
      sudo systemctl daemon-reload sudo systemctl enable performance_governors.service sudo systemctl start performance_governors.service
  7. Verify Operation: Check the service status and the governor settings:
    sudo systemctl status performance_governors.service sudo /usr/local/bin/performance_governors.sh status

    You can also check individual governor files, e.g., cat /sys/devices/system/cpu/cpufreq/policy0/scaling_governor.

4. Using the Script Manually

Once installed, you can also manage the governors manually using the script:

  • Set to performance: sudo /usr/local/bin/performance_governors.sh start
  • Restore defaults: sudo /usr/local/bin/performance_governors.sh stop
  • Check status: sudo /usr/local/bin/performance_governors.sh status
  • Restart (stop then start): sudo /usr/local/bin/performance_governors.sh restart
URL: https://ib.bsb.br/dynamic-performance-governor-management-script-with-systemd