summaryrefslogtreecommitdiff
path: root/gnu/services/linux.scm
diff options
context:
space:
mode:
authorGiacomo Leidi <therewasa@fishinthecalculator.me>2026-01-04 23:58:56 +0100
committerGiacomo Leidi <therewasa@fishinthecalculator.me>2026-03-07 22:28:01 +0100
commitfd6b17bf20dc0cec2b4edff2928f899ebd2741cc (patch)
treecc0ffd987b0f6e2766567ff0b83d7cc4a11652c9 /gnu/services/linux.scm
parent8ecab809db0584eb27fcde7c66bea5e8beb9fbc3 (diff)
gnu: Add tuned-service-type.
* gnu/services/linux.scm (tuned-configuration,tuned-settings,tuned-ppd-settings): New configuration records. (tuned-file-systems,tuned-activation,tuned-shepherd-services, tuned-kernel-modules): New procedures. (tuned-service-type): New service type. * doc/guix.texi: Add service documentation. Change-Id: I6c8d54c23175c2ea133d99965641c548fb1d6452
Diffstat (limited to 'gnu/services/linux.scm')
-rw-r--r--gnu/services/linux.scm448
1 files changed, 448 insertions, 0 deletions
diff --git a/gnu/services/linux.scm b/gnu/services/linux.scm
index 90693b77eb3..2284741a659 100644
--- a/gnu/services/linux.scm
+++ b/gnu/services/linux.scm
@@ -8,6 +8,7 @@
8;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu> 8;;; Copyright © 2023 Bruno Victal <mirai@makinata.eu>
9;;; Copyright © 2023 Felix Lechner <felix.lechner@lease-up.com> 9;;; Copyright © 2023 Felix Lechner <felix.lechner@lease-up.com>
10;;; Copyright © 2025 Edouard Klein <edk@beaver-labs.com> 10;;; Copyright © 2025 Edouard Klein <edk@beaver-labs.com>
11;;; Copyright © 2026 Giacomo Leidi <therewasa@fishinthecalculator.me>
11;;; 12;;;
12;;; This file is part of GNU Guix. 13;;; This file is part of GNU Guix.
13;;; 14;;;
@@ -30,13 +31,18 @@
30 #:use-module (guix records) 31 #:use-module (guix records)
31 #:use-module (guix modules) 32 #:use-module (guix modules)
32 #:use-module (guix i18n) 33 #:use-module (guix i18n)
34 #:use-module (guix packages)
33 #:use-module (guix ui) 35 #:use-module (guix ui)
34 #:use-module (gnu services) 36 #:use-module (gnu services)
35 #:use-module (gnu services admin) 37 #:use-module (gnu services admin)
36 #:use-module (gnu services base) 38 #:use-module (gnu services base)
37 #:use-module (gnu services configuration) 39 #:use-module (gnu services configuration)
40 #:use-module (gnu services dbus)
38 #:use-module (gnu services shepherd) 41 #:use-module (gnu services shepherd)
42 #:use-module (gnu system file-systems)
43 #:use-module (gnu packages base)
39 #:use-module (gnu packages linux) 44 #:use-module (gnu packages linux)
45 #:use-module (gnu packages power)
40 #:use-module (gnu packages file-systems) 46 #:use-module (gnu packages file-systems)
41 #:use-module (srfi srfi-1) 47 #:use-module (srfi srfi-1)
42 #:use-module (srfi srfi-26) 48 #:use-module (srfi srfi-26)
@@ -45,6 +51,7 @@
45 #:use-module (srfi srfi-171) 51 #:use-module (srfi srfi-171)
46 #:use-module (ice-9 format) 52 #:use-module (ice-9 format)
47 #:use-module (ice-9 match) 53 #:use-module (ice-9 match)
54 #:use-module (ice-9 string-fun)
48 #:export (earlyoom-configuration 55 #:export (earlyoom-configuration
49 earlyoom-configuration? 56 earlyoom-configuration?
50 earlyoom-configuration-earlyoom 57 earlyoom-configuration-earlyoom
@@ -97,6 +104,49 @@
97 rasdaemon-configuration-record? 104 rasdaemon-configuration-record?
98 rasdaemon-service-type 105 rasdaemon-service-type
99 106
107 %default-tuned-configuration-recommend.conf
108 %default-tuned-ppd-settings-battery
109 %default-tuned-ppd-settings-profiles
110
111 tuned-configuration
112 tuned-configuration?
113 tuned-configuration-fields
114 tuned-configuration-tuned
115 tuned-configuration-auto-start?
116 tuned-configuration-power-profiles-daemon-support?
117 tuned-configuration-profiles
118 tuned-configuration-settings
119 tuned-configuration-ppd-settings
120 tuned-configuration-recommend.conf
121
122 tuned-settings
123 tuned-settings?
124 tuned-settings-fields
125 tuned-settings-daemon?
126 tuned-settings-dynamic-tuning?
127 tuned-settings-default-instance-priority
128 tuned-settings-recommend-command?
129 tuned-settings-sleep-interval
130 tuned-settings-update-interval
131 tuned-settings-profile-dirs
132 tuned-settings-extra-content
133
134 tuned-ppd-settings
135 tuned-ppd-settings?
136 tuned-ppd-settings-fields
137 tuned-ppd-settings-default
138 tuned-ppd-settings-battery-detection?
139 tuned-ppd-settings-sysfs-acpi-monitor?
140 tuned-ppd-settings-profiles
141 tuned-ppd-settings-battery
142 tuned-ppd-settings-extra-content
143
144 tuned-file-systems
145 tuned-activation
146 tuned-shepherd-services
147
148 tuned-service-type
149
100 zram-device-configuration 150 zram-device-configuration
101 zram-device-configuration? 151 zram-device-configuration?
102 zram-device-configuration-size 152 zram-device-configuration-size
@@ -551,6 +601,404 @@ the Linux @code{cachefiles} module.")
551 601
552 602
553;;; 603;;;
604;;; TuneD.
605;;;
606
607(define (uglify-snake-case field-name)
608 "Serializes FIELD-NAME, a field name from @code{(gnu services configuration)},
609to a snake case string representation of the field name. Trailing @code{?} in
610the name are dropped and @code{-} get replaced by @code{_}.
611
612For example the procedure would convert @code{'A-Field?} to @code{\"a_field\"}."
613 (define str (symbol->string field-name))
614 (string-downcase
615 (string-replace-substring
616 (if (string-suffix? "?" str)
617 (string-drop-right str 1)
618 str)
619 "-" "_")))
620
621(define* (tuned-serialize-pair pair #:key (separator "="))
622 (define (valid? member)
623 (or (string? member)
624 (gexp? member)
625 (file-like? member)))
626 (match pair
627 (((? valid? key) . (? valid? value))
628 #~(string-append #$key #$separator #$value))
629 (_
630 (raise
631 (formatted-message
632 (G_ "pair members must contain only strings, gexps or file-like objects
633but ~a was found")
634 pair)))))
635
636(define* (tuned-ppd-serialize-mixed-list name value #:key (separator " = "))
637 (if (zero? (length value))
638 ""
639 #~(string-append "\n[" #$(uglify-snake-case name) "]\n"
640 (string-join
641 (list #$@(map tuned-serialize-pair value)) "\n")
642 "\n")))
643
644(define (mixed-list? value)
645 ;; Expected spec format:
646 ;; '(("name" . "value") "name=value")
647 (for-each
648 (lambda (el)
649 (cond ((string? el) el)
650 ((pair? el) (tuned-serialize-pair el))
651 (else
652 (raise
653 (formatted-message
654 (G_ "members must be either strings or pairs but ~a was
655found!")
656 el)))))
657 value)
658 #t)
659
660(define (tuned-ppd-serialize-string name value)
661 (format #f "~a=~a" (uglify-snake-case name) value))
662
663(define (tuned-ppd-serialize-boolean name value)
664 (format #f "~a=~a" (uglify-snake-case name) (if value "true" "false")))
665
666(define %default-tuned-ppd-settings-profiles
667 '(("power-saver" . "powersave")
668 ("balanced" . "balanced")
669 ("performance" . "throughput-performance")))
670
671(define %default-tuned-ppd-settings-battery
672 '(("balanced" . "balanced-battery")))
673
674(define-configuration tuned-ppd-settings
675 (default
676 (string "balanced")
677 "Default PPD profile.")
678 (battery-detection?
679 (boolean #t)
680 "Whether to enable battery detection.")
681 (sysfs-acpi-monitor?
682 (boolean #t)
683 "Whether to react to changes of ACPI platform profile done via function keys
684(e.g., Fn-L). This is marked upstream as an experimental feature.")
685 (profiles
686 (mixed-list %default-tuned-ppd-settings-profiles)
687 "Map of PPD profiles states to TuneD profiles. It's supposed to be a list of
688pairs, pair members are supposed to be string. It defaults to
689@code{%default-tuned-ppd-settings-profiles}:
690
691@lisp
692'((\"power-saver\" . \"powersave\")
693 (\"balanced\" . \"balanced\")
694 (\"performance\" . \"throughput-performance\"))
695@end lisp
696
697Elements can be pairs or strings. Pair members can be either strings, gexps or
698file like objects. Strings are directly passed to the serializer. This can be
699an escape hatch in case the underlying syntax of the output file changes
700slightly and the Scheme API is not adequated in time. This way there is always
701a way to work around Scheme records.")
702 (battery
703 (mixed-list %default-tuned-ppd-settings-battery)
704 "Map of PPD battery states to TuneD profiles. It's supposed to be a list of
705pairs, pair members are supposed to be string. It defaults to
706@code{%default-tuned-ppd-settings-battery}:
707
708@lisp
709'((\"balanced\" . \"balanced-battery\"))
710@end lisp
711
712Elements can be pairs or strings. Pair members can be either strings, gexps or
713file like objects. Strings are directly passed to the serializer. This can be
714an escape hatch in case the underlying syntax of the output file changes
715slightly and the Scheme API is not adequated in time. This way there is always
716a way to work around Scheme records.")
717 (extra-content
718 (text-config
719 (list (plain-file "tuned-ppd-settings-extra-content" "")))
720 "A list of file-like objects that are appended to the configuration file."
721 (serializer serialize-text-config))
722 (prefix tuned-ppd-))
723
724(define (serialize-tuned-ppd-settings config)
725 (define fields
726 (filter-configuration-fields
727 tuned-ppd-settings-fields
728 '(default battery-detection? sysfs-acpi-monitor? profiles battery
729 extra-content)))
730 (define getters
731 (map configuration-field-getter fields))
732 (define names
733 (map configuration-field-name fields))
734 (define serializers
735 (map configuration-field-serializer fields))
736 (define values
737 (map (match-lambda ((serializer name getter)
738 (serializer name (getter config))))
739 (zip serializers names getters)))
740
741 (match values
742 ((default battery-detection? sysfs-acpi-monitor? profiles battery
743 extra-content)
744
745 (mixed-text-file "ppd.conf"
746 "[main]\n"
747 (string-append default "\n")
748 (string-append battery-detection? "\n")
749 (string-append sysfs-acpi-monitor? "\n")
750 "\n"
751 profiles
752 battery
753 extra-content
754 "\n"))))
755
756(define (serialize-list-of-profile-dirs name value)
757 (if (zero? (length value))
758 ""
759 #~(string-append
760 #$(uglify-snake-case name) " = " (string-join (list #$@value) ","))))
761
762(define (list-of-profile-dirs? value)
763 (for-each
764 (lambda (el)
765 (unless (or (string? el) (file-like? el))
766 (raise
767 (formatted-message
768 (G_ "tuned-settings profile-dirs members must be either a string
769or a file-like object but ~a was found!")
770 el))))
771 value)
772 #t)
773
774(define tuned-serialize-boolean tuned-ppd-serialize-boolean)
775(define tuned-serialize-integer tuned-ppd-serialize-string)
776
777(define-configuration tuned-settings
778 (daemon?
779 (boolean #t)
780 "Whether to use daemon. Without daemon TuneD just applies tuning.")
781 (dynamic-tuning?
782 (boolean #f)
783 "Dynamically tune devices, if disabled only static tuning will be used.")
784 (default-instance-priority
785 (integer 0)
786 "Default priority assigned to instances.")
787 (recommend-command?
788 (boolean #t)
789 "Recommend functionality, if disabled @code{recommend} command will be not
790available in CLI, daemon will not parse @file{recommend.conf} but will return
791one hardcoded profile (by default @code{balanced}).")
792 (sleep-interval
793 (integer 1)
794 "How long to sleep before checking for events (in seconds),
795higher number means lower overhead but longer response time.")
796 (update-interval
797 (integer 10)
798 "Update interval for dynamic tunings (in seconds). It must be a multiple of
799the @code{sleep-interval}.")
800 (profile-dirs
801 (list-of-profile-dirs
802 (list (file-append tuned "/lib/tuned/profiles") "/etc/tuned/profiles"))
803 "List of strings or gexps representing directories to search for
804profiles. In case of collisions in profile names, the latter directory takes
805precedence."
806 (serializer serialize-list-of-profile-dirs))
807 (extra-content
808 (text-config
809 (list (plain-file "tuned-settings-extra-content" "")))
810 "A list of file-like objects that are appended to the configuration file."
811 (serializer serialize-text-config))
812 (prefix tuned-))
813
814(define (serialize-tuned-settings config)
815 (define fields
816 (filter-configuration-fields
817 tuned-settings-fields
818 '(daemon? dynamic-tuning? default-instance-priority
819 recommend-command? sleep-interval update-interval profile-dirs
820 extra-content)))
821 (define getters
822 (map configuration-field-getter fields))
823 (define names
824 (map configuration-field-name fields))
825 (define serializers
826 (map configuration-field-serializer fields))
827 (define values
828 (map (match-lambda ((serializer name getter)
829 (serializer name (getter config))))
830 (zip serializers names getters)))
831
832 (match values
833 ((daemon? dynamic-tuning? default-instance-priority
834 recommend-command? sleep-interval update-interval profile-dirs
835 extra-content)
836 (mixed-text-file
837 "tuned-main.conf"
838 (string-append daemon? "\n\n")
839 (string-append dynamic-tuning? "\n\n")
840 (string-append default-instance-priority "\n\n")
841 (string-append recommend-command? "\n\n")
842 (string-append sleep-interval "\n\n")
843 (string-append update-interval "\n\n")
844 profile-dirs
845 extra-content
846 "\n"))))
847
848(define (tuned-plugin? value)
849 (if (and (= 2 (length value))
850 (string? (first value))
851 (file-like? (second value)))
852 #t
853 (raise
854 (formatted-message
855 (G_ "tuned-configuration profiles members must be lists with two
856elements, the first being a string and the second a file-like object, but ~a was
857found!")
858 value))))
859
860(define (list-of-tuned-plugins? value)
861 (list-of tuned-plugin?))
862
863(define %default-tuned-configuration-recommend.conf
864 (file-append tuned "/lib/tuned/recommend.d/50-tuned.conf"))
865
866(define-configuration/no-serialization tuned-configuration
867 (tuned
868 (package tuned)
869 "The TuneD package.")
870 (auto-start?
871 (boolean #t)
872 "Whether this service should be started automatically by the Shepherd. If it
873is @code{#f} the service has to be started manually with @command{herd start}.")
874 (power-profiles-daemon-support?
875 (boolean #f)
876 "Whether the power-profiles-daemon emulation layer should be
877enabled.")
878 (profiles
879 (list-of-tuned-plugins '())
880 "User provided profiles for TuneD. Each element of the list is supposed to be
881a list where the first element is the name of the directory where plugin files
882will be placed under @file{/etc/tuned/profiles} and the second a file like
883object containing the plugin files:
884
885@lisp
886(list
887 (list \"plugin-name\"
888 (plain-file \"plugin.conf\" \"content\"))
889 (list \"other-plugin\"
890 (file-union \"plugin-data\"
891 (list (list \"other-plugin.conf\"
892 (plain-file \"other-plugin.conf\" \"content\"))
893 (list \"other-plugin.scm\"
894 (program-file \"other-plugin.scm\"
895 #~(display \"content\")))))))
896@end lisp")
897 (settings
898 (tuned-settings (tuned-settings))
899 "Configuration for TuneD.")
900 (ppd-settings
901 (tuned-ppd-settings (tuned-ppd-settings))
902 "Configuration for the @code{power-profiles-daemon} compatibility layer of
903TuneD.")
904 (recommend.conf
905 (file-like %default-tuned-configuration-recommend.conf)
906 "File like object containing the recommended profile configuration. Defaults
907to @code{%default-tuned-configuration-recommend.conf}."))
908
909(define (tuned-file-systems config)
910 (list
911 (file-system
912 (device "none")
913 (mount-point "/run/tuned")
914 (type "ramfs")
915 (check? #f))))
916
917(define (tuned-activation config)
918 (match-record config <tuned-configuration>
919 (profiles settings ppd-settings recommend.conf)
920 #~(begin
921 (use-modules (guix build utils))
922 (let ((data-directory "/var/lib/tuned")
923 (config-directory "/etc/tuned"))
924 ;; Setup TuneD directories.
925 (for-each
926 (lambda (dir)
927 (mkdir-p dir)
928 (chmod dir #o755))
929 (list data-directory config-directory
930 ;; the directory where TuneD will look for kernel modules for
931 ;; plugins.
932 "/etc/tuned/modprobe.d"))
933 ;; Create plugins kernel modules configuration.
934 (invoke #$(file-append coreutils-minimal "/bin/touch")
935 "/etc/tuned/modprobe.d/tuned.conf")
936 ;; Generate and activate TuneD configuration files.
937 ;; TuneD needs to write in /etc/tuned, special-files-service-type
938 ;; creates a file union, so /etc/tuned is read-only and TuneD crashes.
939 ;; activate-special-files creates a single symlink to the store for
940 ;; each file so TuneD doesn't notice and runs successfully.
941 (activate-special-files
942 '(("/etc/tuned/recommend.conf" #$recommend.conf)
943 ("/etc/tuned/profiles"
944 #$(file-union "tuned-profiles" profiles))
945 ("/etc/tuned/tuned-main.conf"
946 #$(serialize-tuned-settings settings))
947 ("/etc/tuned/ppd.conf"
948 #$(serialize-tuned-ppd-settings ppd-settings))))))))
949
950(define (tuned-shepherd-services config)
951 (match-record config <tuned-configuration>
952 (tuned auto-start? power-profiles-daemon-support?)
953 (append
954 (list
955 (shepherd-service
956 (documentation "TuneD daemon")
957 (provision '(tuned))
958 (requirement '(dbus-system user-processes udev))
959 (start #~(make-forkexec-constructor
960 '(#$(file-append tuned "/sbin/tuned"))
961 #:log-file "/var/log/tuned/tuned.log"))
962 (stop #~(make-kill-destructor))
963 (auto-start? auto-start?)))
964 (if power-profiles-daemon-support?
965 (list (shepherd-service
966 (documentation "TuneD power-profiles-daemon emulation daemon")
967 (provision '(tuned-ppd power-profiles-daemon))
968 (requirement '(dbus-system user-processes udev tuned))
969 (start
970 #~(make-forkexec-constructor
971 '(#$(file-append tuned "/sbin/tuned-ppd"))
972 #:log-file "/var/log/tuned/tuned-ppd.log"))
973 (stop #~(make-kill-destructor))
974 (auto-start? auto-start?)))
975 '()))))
976
977(define tuned-service-type
978 (let ((config->package
979 (compose list tuned-configuration-tuned)))
980 (service-type
981 (name 'tuned)
982 (extensions (list
983 (service-extension shepherd-root-service-type
984 tuned-shepherd-services)
985 (service-extension dbus-root-service-type
986 config->package)
987 (service-extension polkit-service-type
988 config->package)
989 (service-extension profile-service-type
990 config->package)
991 (service-extension file-system-service-type
992 tuned-file-systems)
993 (service-extension activation-service-type
994 tuned-activation)))
995 (default-value (tuned-configuration))
996 (description "Run the TuneD daemon. It is daemon that tunes system settings
997dynamically. It does so by monitoring the usage of several system components
998periodically."))))
999
1000
1001;;;
554;;; Zram device 1002;;; Zram device
555;;; 1003;;;
556(define-record-type* <zram-device-configuration> 1004(define-record-type* <zram-device-configuration>