;;; GNU Guix --- Functional package management for GNU ;;; Copyright © 2017 Mathieu Othacehe ;;; Copyright © 2024 Ian Eure ;;; ;;; This file is part of GNU Guix. ;;; ;;; GNU Guix is free software; you can redistribute it and/or modify ;;; it under the terms of the GNU General Public License as published by ;;; the Free Software Foundation, either version 3 of the License, or ;;; (at your option) any later version. ;;; ;;; GNU Guix is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;;; GNU General Public License for more details. ;;; ;;; You should have received a copy of the GNU General Public License ;;; along with GNU Guix. If not, see . (define-module (gnu services pm) #:use-module (srfi srfi-1) #:use-module (ice-9 match) #:use-module (guix gexp) #:use-module (guix packages) #:use-module (guix records) #:use-module (gnu packages admin) #:use-module (gnu packages linux) #:use-module (gnu services) #:use-module (gnu services base) #:use-module (gnu services configuration) #:use-module (gnu services shepherd) #:use-module (gnu system shadow) #:export (tlp-service-type tlp-configuration thermald-configuration thermald-service-type powertop-configuration powertop-service-type)) (define (uglify-field-name field-name) (let ((str (symbol->string field-name))) (string-join (string-split (string-upcase (if (string-suffix? "?" str) (substring str 0 (1- (string-length str))) str)) #\-) "_"))) (define (serialize-field field-name val) (format #t "~a=~a\n" (uglify-field-name field-name) val)) (define (serialize-boolean field-name val) (serialize-field field-name (if val "1" "0"))) (define-maybe boolean) (define (serialize-string field-name val) (serialize-field field-name val)) (define-maybe string) (define (space-separated-string-list? val) (and (list? val) (and-map (lambda (x) (and (string? x) (not (string-index x #\space)))) val))) (define (serialize-space-separated-string-list field-name val) (serialize-field field-name (format #f "~s" (string-join val " ")))) (define-maybe space-separated-string-list) (define (non-negative-integer? val) (and (exact-integer? val) (not (negative? val)))) (define (serialize-non-negative-integer field-name val) (serialize-field field-name val)) (define-maybe non-negative-integer) (define (on-off-boolean? val) (boolean? val)) (define (serialize-on-off-boolean field-name val) (serialize-field field-name (if val "on" "off"))) (define-maybe on-off-boolean) (define (y-n-boolean? val) (boolean? val)) (define (serialize-y-n-boolean field-name val) (serialize-field field-name (if val "Y" "N"))) (define-configuration tlp-configuration (tlp (file-like tlp) "The TLP package.") (tlp-enable? (boolean #t) "Set to true if you wish to enable TLP.") (tlp-default-mode (string "AC") "Default mode when no power supply can be detected. Alternatives are AC and BAT.") (disk-idle-secs-on-ac (non-negative-integer 0) "Number of seconds Linux kernel has to wait after the disk goes idle, before syncing on AC.") (disk-idle-secs-on-bat (non-negative-integer 2) "Same as @code{disk-idle-ac} but on BAT mode.") (max-lost-work-secs-on-ac (non-negative-integer 15) "Dirty pages flushing periodicity, expressed in seconds.") (max-lost-work-secs-on-bat (non-negative-integer 60) "Same as @code{max-lost-work-secs-on-ac} but on BAT mode.") (cpu-scaling-governor-on-ac maybe-space-separated-string-list "CPU frequency scaling governor on AC mode. With intel_pstate driver, alternatives are powersave and performance. With acpi-cpufreq driver, alternatives are ondemand, powersave, performance and conservative.") (cpu-scaling-governor-on-bat maybe-space-separated-string-list "Same as @code{cpu-scaling-governor-on-ac} but on BAT mode.") (cpu-scaling-min-freq-on-ac maybe-non-negative-integer "Set the min available frequency for the scaling governor on AC.") (cpu-scaling-max-freq-on-ac maybe-non-negative-integer "Set the max available frequency for the scaling governor on AC.") (cpu-scaling-min-freq-on-bat maybe-non-negative-integer "Set the min available frequency for the scaling governor on BAT.") (cpu-scaling-max-freq-on-bat maybe-non-negative-integer "Set the max available frequency for the scaling governor on BAT.") (cpu-min-perf-on-ac maybe-non-negative-integer "Limit the min P-state to control the power dissipation of the CPU, in AC mode. Values are stated as a percentage of the available performance.") (cpu-max-perf-on-ac maybe-non-negative-integer "Limit the max P-state to control the power dissipation of the CPU, in AC mode. Values are stated as a percentage of the available performance.") (cpu-min-perf-on-bat maybe-non-negative-integer "Same as @code{cpu-min-perf-on-ac} on BAT mode.") (cpu-max-perf-on-bat maybe-non-negative-integer "Same as @code{cpu-max-perf-on-ac} on BAT mode.") (cpu-boost-on-ac? maybe-boolean "Enable CPU turbo boost feature on AC mode.") (cpu-boost-on-bat? maybe-boolean "Same as @code{cpu-boost-on-ac?} on BAT mode.") (sched-powersave-on-ac? (boolean #f) "Allow Linux kernel to minimize the number of CPU cores/hyper-threads used under light load conditions.") (sched-powersave-on-bat? (boolean #t) "Same as @code{sched-powersave-on-ac?} but on BAT mode.") (nmi-watchdog? (boolean #f) "Enable Linux kernel NMI watchdog.") (phc-controls maybe-string "For Linux kernels with PHC patch applied, change CPU voltages. An example value would be @samp{\"F:V F:V F:V F:V\"}.") (energy-perf-policy-on-ac (string "performance") "Set CPU performance versus energy saving policy on AC. Alternatives are performance, normal, powersave.") (energy-perf-policy-on-bat (string "powersave") "Same as @code{energy-perf-policy-ac} but on BAT mode.") (disks-devices (space-separated-string-list '("sda")) "Hard disk devices.") (disk-apm-level-on-ac (space-separated-string-list '("254" "254")) "Hard disk advanced power management level.") (disk-apm-level-on-bat (space-separated-string-list '("128" "128")) "Same as @code{disk-apm-bat} but on BAT mode.") (disk-spindown-timeout-on-ac maybe-space-separated-string-list "Hard disk spin down timeout. One value has to be specified for each declared hard disk.") (disk-spindown-timeout-on-bat maybe-space-separated-string-list "Same as @code{disk-spindown-timeout-on-ac} but on BAT mode.") (disk-iosched maybe-space-separated-string-list "Select IO scheduler for disk devices. One value has to be specified for each declared hard disk. Example alternatives are cfq, deadline and noop.") (sata-linkpwr-on-ac (string "max_performance") "SATA aggressive link power management (ALPM) level. Alternatives are min_power, medium_power, max_performance.") (sata-linkpwr-on-bat (string "min_power") "Same as @code{sata-linkpwr-ac} but on BAT mode.") (sata-linkpwr-blacklist maybe-string "Exclude specified SATA host devices for link power management.") (ahci-runtime-pm-on-ac? maybe-on-off-boolean "Enable Runtime Power Management for AHCI controller and disks on AC mode.") (ahci-runtime-pm-on-bat? maybe-on-off-boolean "Same as @code{ahci-runtime-pm-on-ac} on BAT mode.") (ahci-runtime-pm-timeout (non-negative-integer 15) "Seconds of inactivity before disk is suspended.") (pcie-aspm-on-ac (string "performance") "PCI Express Active State Power Management level. Alternatives are default, performance, powersave.") (pcie-aspm-on-bat (string "powersave") "Same as @code{pcie-aspm-ac} but on BAT mode.") (start-charge-thresh-bat0 maybe-non-negative-integer "Percentage when battery 0 should begin charging.") (stop-charge-thresh-bat0 maybe-non-negative-integer "Percentage when battery 0 should stop charging.") (start-charge-thresh-bat1 maybe-non-negative-integer "Percentage when battery 1 should begin charging.") (stop-charge-thresh-bat1 maybe-non-negative-integer "Percentage when battery 1 should stop charging.") (radeon-power-profile-on-ac (string "high") "Radeon graphics clock speed level. Alternatives are low, mid, high, auto, default.") (radeon-power-profile-on-bat (string "low") "Same as @code{radeon-power-ac} but on BAT mode.") (radeon-dpm-state-on-ac (string "performance") "Radeon dynamic power management method (DPM). Alternatives are battery, performance.") (radeon-dpm-state-on-bat (string "battery") "Same as @code{radeon-dpm-state-ac} but on BAT mode.") (radeon-dpm-perf-level-on-ac (string "auto") "Radeon DPM performance level. Alternatives are auto, low, high.") (radeon-dpm-perf-level-on-bat (string "auto") "Same as @code{radeon-dpm-perf-ac} but on BAT mode.") (wifi-pwr-on-ac? (on-off-boolean #f) "Wifi power saving mode.") (wifi-pwr-on-bat? (on-off-boolean #t) "Same as @code{wifi-power-ac?} but on BAT mode.") (wol-disable? (y-n-boolean #t) "Disable wake on LAN.") (sound-power-save-on-ac (non-negative-integer 0) "Timeout duration in seconds before activating audio power saving on Intel HDA and AC97 devices. A value of 0 disables power saving.") (sound-power-save-on-bat (non-negative-integer 1) "Same as @code{sound-powersave-ac} but on BAT mode.") (sound-power-save-controller? (y-n-boolean #t) "Disable controller in powersaving mode on Intel HDA devices.") (bay-poweroff-on-bat? (boolean #f) "Enable optical drive in UltraBay/MediaBay on BAT mode. Drive can be powered on again by releasing (and reinserting) the eject lever or by pressing the disc eject button on newer models.") (bay-device (string "sr0") "Name of the optical drive device to power off.") (runtime-pm-on-ac (string "on") "Runtime Power Management for PCI(e) bus devices. Alternatives are on and auto.") (runtime-pm-on-bat (string "auto") "Same as @code{runtime-pm-ac} but on BAT mode.") (runtime-pm-all? (boolean #t) "Runtime Power Management for all PCI(e) bus devices, except blacklisted ones.") (runtime-pm-blacklist maybe-space-separated-string-list "Exclude specified PCI(e) device addresses from Runtime Power Management.") (runtime-pm-driver-blacklist (space-separated-string-list '("radeon" "nouveau")) "Exclude PCI(e) devices assigned to the specified drivers from Runtime Power Management.") (usb-autosuspend? (boolean #t) "Enable USB autosuspend feature.") (usb-blacklist maybe-string "Exclude specified devices from USB autosuspend.") (usb-blacklist-wwan? (boolean #t) "Exclude WWAN devices from USB autosuspend.") (usb-whitelist maybe-string "Include specified devices into USB autosuspend, even if they are already excluded by the driver or via @code{usb-blacklist-wwan?}.") (usb-autosuspend-disable-on-shutdown? maybe-boolean "Enable USB autosuspend before shutdown.") (restore-device-state-on-startup? (boolean #f) "Restore radio device state (bluetooth, wifi, wwan) from previous shutdown on system startup.")) (define (tlp-shepherd-service config) (let* ((tlp-bin (file-append (tlp-configuration-tlp config) "/bin/tlp")) (tlp-action (lambda args #~(lambda _ (zero? (system* #$tlp-bin #$@args)))))) (list (shepherd-service (documentation "Run TLP script.") (provision '(tlp)) (requirement '(user-processes)) (start (tlp-action "init" "start")) (stop (tlp-action "init" "stop")))))) (define (tlp-activation config) (let* ((config-str (with-output-to-string (lambda () (serialize-configuration config tlp-configuration-fields)))) (config-file (plain-file "tlp" config-str))) (with-imported-modules '((guix build utils)) #~(begin (use-modules (guix build utils)) (copy-file #$config-file "/etc/tlp.conf"))))) (define tlp-service-type (service-type (name 'tlp) (extensions (list (service-extension shepherd-root-service-type tlp-shepherd-service) (service-extension udev-service-type (compose list tlp-configuration-tlp)) (service-extension activation-service-type tlp-activation))) (default-value (tlp-configuration)) (description "Run TLP, a power management tool."))) (define (generate-tlp-documentation) (generate-documentation `((tlp-configuration ,tlp-configuration-fields)) 'tlp-configuration)) ;;; ;;; thermald ;;; ;;; This service implements cpu scaling. Helps prevent overheating! (define-record-type* thermald-configuration make-thermald-configuration thermald-configuration? (adaptive? thermald-adaptive? ;boolean (default #f)) (ignore-cpuid-check? thermald-ignore-cpuid-check? ;boolean (default #f)) (thermald thermald-thermald ;file-like (default thermald))) (define (thermald-shepherd-service config) (list (shepherd-service (provision '(thermald)) (documentation "Run thermald cpu frequency scaling.") (start #~(make-forkexec-constructor '(#$(file-append (thermald-thermald config) "/sbin/thermald") "--no-daemon" #$@(if (thermald-adaptive? config) '("--adaptive") '()) #$@(if (thermald-ignore-cpuid-check? config) '("--ignore-cpuid-check") '())))) (stop #~(make-kill-destructor))))) (define thermald-service-type (service-type (name 'thermald) (extensions (list (service-extension shepherd-root-service-type thermald-shepherd-service))) (default-value (thermald-configuration)) (description "Run thermald, a CPU frequency scaling service that helps prevent overheating."))) ;;; ;;; powertop ;;; ;;; Calls `powertop --auto-tune' to reduce energy consumption. (define-configuration powertop-configuration (powertop (package powertop) "PowerTOP package to use.")) (define powertop-shepherd-service (match-lambda (($ powertop) (shepherd-service (documentation "Tune kernel power settings at boot.") (provision '(powertop powertop-auto-tune)) (requirement '(user-processes)) (one-shot? #t) (start #~(lambda _ (zero? (system* #$(file-append powertop "/sbin/powertop") "--auto-tune")))))))) (define powertop-service-type (service-type (name 'powertop) (extensions (list (service-extension shepherd-root-service-type (compose list powertop-shepherd-service)))) (compose concatenate) (default-value (powertop-configuration)) (description "Tune power-related kernel parameters to reduce energy consumption.")))