diff --git a/modules/nixos/system/power/default.nix b/modules/nixos/system/power/default.nix index f24d276..9a82104 100644 --- a/modules/nixos/system/power/default.nix +++ b/modules/nixos/system/power/default.nix @@ -7,7 +7,7 @@ ... }: let - cfg = config.${namespace}; + cfg = config.${namespace}.powerManagement; ppd-patched = pkgs.power-profiles-daemon.overrideAttrs ( _finalAttrs: _prevAttrs: { @@ -26,10 +26,36 @@ in options.${namespace}.powerManagement.enable = lib.mkEnableOption "Enables power management, e.g. for laptops."; - # Configure power management via power-profiles-daemon - # https://gitlab.freedesktop.org/upower/power-profiles-daemon - config.services.power-profiles-daemon = lib.mkIf cfg.powerManagement.enable { - enable = true; - package = ppd-patched; + config = { + # Configure power management via power-profiles-daemon + # https://gitlab.freedesktop.org/upower/power-profiles-daemon + services.power-profiles-daemon = lib.mkIf cfg.enable { + enable = true; + package = ppd-patched; + }; + + # Configure power management via tuned + # https://github.com/redhat-performance/tuned/ + environment.systemPackages = [ pkgs.${namespace}.tuned ]; + systemd.services.tuned = { + wantedBy = [ "multi-user.target" ]; + after = [ "dbus.service" ]; + description = "tuned power management daemon."; + enable = true; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.${namespace}.tuned}/bin/tuned"; + }; + }; + systemd.services.tuned-ppd = { + wantedBy = [ "multi-user.target" ]; + after = [ "tuned.service" ]; + description = "tuned power management daemon - power-profiles-daemon compatibility."; + enable = true; + serviceConfig = { + Type = "simple"; + ExecStart = "${pkgs.${namespace}.tuned}/bin/tuned-ppd"; + }; + }; }; } diff --git a/packages/tuned/default.nix b/packages/tuned/default.nix new file mode 100644 index 0000000..db99abd --- /dev/null +++ b/packages/tuned/default.nix @@ -0,0 +1,151 @@ +# Source: https://www.reddit.com/r/NixOS/ +{ + lib, + asciidoctor, + desktop-file-utils, + dmidecode, + ethtool, + fetchFromGitHub, + gawk, + gobject-introspection, + hdparm, + iproute2, + nix-update-script, + pkg-config, + powertop, + python3, + testers, + tuna, + tuned, + util-linux, + virt-what, + wrapGAppsHook3, +}: + +python3.pkgs.buildPythonApplication rec { + pname = "tuned"; + version = "2.24.0"; + pyproject = false; + + outputs = [ + "out" + "doc" + "man" + ]; + + src = fetchFromGitHub { + owner = "redhat-performance"; + repo = "tuned"; + rev = "refs/tags/v${version}"; + hash = "sha256-6x5sRzHZEKS7keEBRqDNQsdAcqrpsZ4h2eTCi1o0hPM="; + }; + + patches = [ + # Some tests require a TTY to run + ./remove-tty-tests.patch + ]; + + postPatch = '' + patchShebangs . + + substituteInPlace Makefile \ + --replace-warn "/usr/bin/powertop2tuned" "/bin/powertop2tuned" \ + --replace-warn "/usr/lib" "/lib" \ + --replace-warn "/usr/sbin" "/bin" + + substituteInPlace tuned-gui.desktop \ + --replace-warn "/usr/sbin/tuned-gui" "tuned-gui" + + substituteInPlace tuned.service \ + --replace-warn "/usr/sbin/tuned" "$out/bin/tuned" + + substituteInPlace experiments/powertop2tuned.py \ + --replace-warn "/usr/sbin/powertop" "${lib.getExe powertop}" + + substituteInPlace tuned/ppd/tuned-ppd.service \ + --replace-warn "/usr/sbin/tuned-ppd" "$out/bin/tuned-ppd" + ''; + + strictDeps = true; + + nativeBuildInputs = [ + asciidoctor + desktop-file-utils + gobject-introspection + pkg-config + wrapGAppsHook3 + ]; + + dependencies = with python3.pkgs; [ + dbus-python + pygobject3 + pyperf + python-linux-procfs + pyudev + tuna + ]; + + makeFlags = [ + "DATADIR=/share" + "DESTDIR=${placeholder "out"}" + "KERNELINSTALLHOOKDIR=/lib/kernel/install.d" + "PYTHON=${lib.getExe python3}" + "PYTHON_SITELIB=/${python3.sitePackages}" + "TMPFILESDIR=/lib/tmpfiles.d" + "TUNED_PROFILESDIR=/lib/tuned/profile" + "UNITDIR=/lib/systemd/system" + ]; + + installTargets = [ + "install" + "install-ppd" + ]; + + postInstall = '' + rm -rf $out/{var,run} + ''; + + dontWrapGApps = true; + makeWrapperArgs = [ + "\${gappsWrapperArgs[@]}" + "--prefix" + "PATH" + ":" + (lib.makeBinPath [ + dmidecode + ethtool + gawk + hdparm + iproute2 + util-linux + virt-what + ]) + ]; + + checkTarget = "test"; + + pythonImportsCheck = [ "tuned" ]; + + # NOTE: `postCheck` is intentionally not used here, as the entire checkPhase + # is skipped by `buildPythonApplication` + # https://github.com/NixOS/nixpkgs/blob/9d4343b7b27a3e6f08fc22ead568233ff24bbbde/pkgs/development/interpreters/python/mk-python-derivation.nix#L296 + postInstallCheck = '' + make $makeFlags $checkTarget + ''; + + passthru = { + tests.version = testers.testVersion { package = tuned; }; + + updateScript = nix-update-script { }; + }; + + meta = { + description = "Tuning Profile Delivery Mechanism for Linux"; + homepage = "https://tuned-project.org"; + changelog = "https://github.com/redhat-performance/tuned/releases/tag/v${version}"; + license = lib.licenses.gpl2Only; + maintainers = with lib.maintainers; [ getchoo ]; + mainProgram = "tuned"; + platforms = lib.platforms.linux; + }; +} diff --git a/packages/tuned/remove-tty-tests.patch b/packages/tuned/remove-tty-tests.patch new file mode 100644 index 0000000..4e6cc02 --- /dev/null +++ b/packages/tuned/remove-tty-tests.patch @@ -0,0 +1,52 @@ +diff --git a/tests/unit/hardware/test_device_matcher_udev.py b/tests/unit/hardware/test_device_matcher_udev.py +index 1903955..f973107 100644 +--- a/tests/unit/hardware/test_device_matcher_udev.py ++++ b/tests/unit/hardware/test_device_matcher_udev.py +@@ -10,27 +10,7 @@ class DeviceMatcherUdevTestCase(unittest.TestCase): + cls.matcher = DeviceMatcherUdev() + + def test_simple_search(self): +- try: +- device = pyudev.Devices.from_sys_path(self.udev_context, +- "/sys/devices/virtual/tty/tty0") +- except AttributeError: +- device = pyudev.Device.from_sys_path(self.udev_context, +- "/sys/devices/virtual/tty/tty0") +- self.assertTrue(self.matcher.match("tty0", device)) +- try: +- device = pyudev.Devices.from_sys_path(self.udev_context, +- "/sys/devices/virtual/tty/tty1") +- except AttributeError: +- device = pyudev.Device.from_sys_path(self.udev_context, +- "/sys/devices/virtual/tty/tty1") +- self.assertFalse(self.matcher.match("tty0", device)) ++ return True + + def test_regex_search(self): +- try: +- device = pyudev.Devices.from_sys_path(self.udev_context, +- "/sys/devices/virtual/tty/tty0") +- except AttributeError: +- device = pyudev.Device.from_sys_path(self.udev_context, +- "/sys/devices/virtual/tty/tty0") +- self.assertTrue(self.matcher.match("tty.", device)) +- self.assertFalse(self.matcher.match("tty[1-9]", device)) ++ return True +diff --git a/tests/unit/hardware/test_inventory.py b/tests/unit/hardware/test_inventory.py +index 8490922..8bd004b 100644 +--- a/tests/unit/hardware/test_inventory.py ++++ b/tests/unit/hardware/test_inventory.py +@@ -18,12 +18,7 @@ class InventoryTestCase(unittest.TestCase): + cls._dummier = DummyPlugin() + + def test_get_device(self): +- try: +- device1 = pyudev.Devices.from_name(self._context, "tty", "tty0") +- except AttributeError: +- device1 = pyudev.Device.from_name(self._context, "tty", "tty0") +- device2 = self._inventory.get_device("tty", "tty0") +- self.assertEqual(device1,device2) ++ return True + + def test_get_devices(self): + device_list1 = self._context.list_devices(subsystem = "tty") \ No newline at end of file