Power Management: Difference between revisions

From NixOS Wiki
imported>Lelgenio
Add workaround for systems that immediatly wakeup after beign suspended
Onny (talk | contribs)
 
(3 intermediate revisions by 2 users not shown)
Line 1: Line 1:
{{expansion}}
This article covers configurations related to power management in terms of energy saving modes of various devices and components.


== Suspend ==
== Configuration ==
 
=== Hard drives ===
Following snippet configures [[Udev]] rules which automatically run the program <code>hdparm</code> to enable power saving modes for hard disks, especially rotational drives mapped to <code>/dev/sd*</code>.<syntaxhighlight lang="nix">
services.udev.extraRules =
  let
    mkRule = as: lib.concatStringsSep ", " as;
    mkRules = rs: lib.concatStringsSep "\n" rs;
  in mkRules ([( mkRule [
    ''ACTION=="add|change"''
    ''SUBSYSTEM=="block"''
    ''KERNEL=="sd[a-z]"''
    ''ATTR{queue/rotational}=="1"''
    ''RUN+="${pkgs.hdparm}/bin/hdparm -B 90 -S 41 /dev/%k"''
  ])]);
</syntaxhighlight>The <code>hdparm</code> parameters <code>-B</code> and <code>-S</code> define power saving modes and in case of <code>-S</code> the standby (spindown) timeout. The number 41 means therefore: Turn off the motor after 205 = 41*5 seconds.


=== Suspend hooks ===
=== Suspend hooks ===
NixOS provides the {{nixos:option|powerManagement.resumeCommands}} option which defines commands that are added to a global script that will be executed after resuming.
NixOS provides the {{nixos:option|powerManagement.resumeCommands}} option which defines commands that are added to a global script that will be executed after resuming.


Line 13: Line 27:
</syntaxhighlight>
</syntaxhighlight>


It is also possible, to use the <code>post-resume</code> target directly to make a service.
It is also possible to use the <code>post-resume</code> target directly to make a service.
Refer to the [[Systemd Services]] article for details about writing systemd services for NixOS.


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
Line 28: Line 41:
</syntaxhighlight>
</syntaxhighlight>


=== Troubleshooting ===
=== Hibernation ===
 
Hibernation requires a configured swap device. See [https://nixos.org/manual/nixos/stable/#ch-installation installation instructions] on how to create a swap partition. An example configuration could look like this:<syntaxhighlight lang="nix">
swapDevices = [
  {
    device = "/dev/hda7";
  }
];
boot.resumeDevice = "/dev/hda7";
</syntaxhighlight>Please note that encrypted swap devices or swap files are not yet supported for hibernation.
 
Test and use hibernation with following command:<syntaxhighlight lang="nix">
systemctl hibernate
</syntaxhighlight>


==== System Immediately wakes up from suspend ====
== Tips and tricks ==


Particularly in some gigabyte motherboards with NVMe drives, the system may immediately wake up from being suspended.
=== Go into hibernate after specific suspend time ===
Using following configuration, your system will go from suspend into hibernate after 1 hour:<syntaxhighlight lang="nix">
systemd.sleep.extraConfig = ''
  HibernateDelaySec=1h
'';
</syntaxhighlight>
 
== Troubleshooting ==
 
==== System immediately wakes up from suspend ====
 
Particularly in some Gigabyte motherboards with NVMe drives, the system may immediately wake up from being suspended.
This can be worked around by disabling the wakeup triggers for the offending components:
This can be worked around by disabling the wakeup triggers for the offending components:


===== Solution 1: Disabling wakeup triggers for all PCIe devices =====
===== Solution 1: Disabling wakeup triggers for all PCIe devices =====


If you don't need your system to wakeup via PCIe components you can simply disable it for all without needing to search for what component is causing problems.
If you don't need your system to wakeup via PCIe components you can simply disable it for all without needing to determine which component is causing problems.


<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
Line 45: Line 82:
</syntaxhighlight>
</syntaxhighlight>


===== Solution 2: Disabling a common NVMe interface =====
===== Solution 2: Disable a common NVMe interface =====


Specifically on Gigabyte motherboards you can try targetting only the NVMe ports.
Specifically on Gigabyte motherboards you can try targetting only the NVMe ports.
Line 55: Line 92:
</syntaxhighlight>
</syntaxhighlight>


===== Solution 3: Disabling a single device's wakeup triggers =====
===== Solution 3: Disable a single device's wakeup triggers =====
If you wish to be more granular in what components should no longer be able to wakeup your system you can manually search what component is causing the wakeup events.
If you wish to be more granular in what components should no longer be able to wakeup your system, you can find out which component is causing the wakeup events.


First, list all components and their current wakeup status:
First, list all components and their current wakeup status:
Line 72: Line 109:
</syntaxhighlight>
</syntaxhighlight>


You can temporarily toggle a device by sending it's "Device" collumn back into `/proc/acpi/wakeup`
You can temporarily toggle a device by writing its "Device" name back into <code>/proc/acpi/wakeup</code>


<syntaxhighlight lang="sh">
<syntaxhighlight lang="sh">
Line 87: Line 124:
</syntaxhighlight>
</syntaxhighlight>


And finally use those values in a udev rule:
And finally use those values in a <code>udev</code> rule:
   
   
<syntaxhighlight lang="nix">
<syntaxhighlight lang="nix">
Line 94: Line 131:
'';
'';
</syntaxhighlight>
</syntaxhighlight>
== See also ==
* [[Laptop]]


== External resources ==
== External resources ==


* {{manual:nixos|sec=#sec-rebooting|chapter=Chapter 23. Rebooting and Shutting Down}}
* {{manual:nixos|sec=#sec-rebooting|chapter=Chapter 23. Rebooting and Shutting Down}}

Latest revision as of 19:23, 29 July 2024

This article covers configurations related to power management in terms of energy saving modes of various devices and components.

Configuration

Hard drives

Following snippet configures Udev rules which automatically run the program hdparm to enable power saving modes for hard disks, especially rotational drives mapped to /dev/sd*.

services.udev.extraRules = 
  let
    mkRule = as: lib.concatStringsSep ", " as;
    mkRules = rs: lib.concatStringsSep "\n" rs;
  in mkRules ([( mkRule [
    ''ACTION=="add|change"''
    ''SUBSYSTEM=="block"''
    ''KERNEL=="sd[a-z]"''
    ''ATTR{queue/rotational}=="1"''
    ''RUN+="${pkgs.hdparm}/bin/hdparm -B 90 -S 41 /dev/%k"''
  ])]);

The hdparm parameters -B and -S define power saving modes and in case of -S the standby (spindown) timeout. The number 41 means therefore: Turn off the motor after 205 = 41*5 seconds.

Suspend hooks

NixOS provides the powerManagement.resumeCommands option which defines commands that are added to a global script that will be executed after resuming.

powerManagement.resumeCommands = ''
  echo "This should show up in the journal after resuming."
'';

It is also possible to use the post-resume target directly to make a service.

  systemd.services.your-service-name = { 
    description = "Service description here";
    wantedBy = [ "post-resume.target" ];
    after = [ "post-resume.target" ];
    script = ''
    echo "This should show up in the journal after resuming."
    '';
    serviceConfig.Type = "oneshot";
  };

Hibernation

Hibernation requires a configured swap device. See installation instructions on how to create a swap partition. An example configuration could look like this:

swapDevices = [
  {
    device = "/dev/hda7";
  }
];
boot.resumeDevice = "/dev/hda7";

Please note that encrypted swap devices or swap files are not yet supported for hibernation. Test and use hibernation with following command:

systemctl hibernate

Tips and tricks

Go into hibernate after specific suspend time

Using following configuration, your system will go from suspend into hibernate after 1 hour:

systemd.sleep.extraConfig = ''
  HibernateDelaySec=1h
'';

Troubleshooting

System immediately wakes up from suspend

Particularly in some Gigabyte motherboards with NVMe drives, the system may immediately wake up from being suspended. This can be worked around by disabling the wakeup triggers for the offending components:

Solution 1: Disabling wakeup triggers for all PCIe devices

If you don't need your system to wakeup via PCIe components you can simply disable it for all without needing to determine which component is causing problems.

services.udev.extraRules = ''
  ACTION=="add", SUBSYSTEM=="pci", DRIVER=="pcieport", ATTR{power/wakeup}="disabled"
'';
Solution 2: Disable a common NVMe interface

Specifically on Gigabyte motherboards you can try targetting only the NVMe ports.

services.udev.extraRules = ''
  ACTION=="add" SUBSYSTEM=="pci" ATTR{vendor}=="0x1022" ATTR{device}=="0x1483" ATTR{power/wakeup}="disabled"
'';
Solution 3: Disable a single device's wakeup triggers

If you wish to be more granular in what components should no longer be able to wakeup your system, you can find out which component is causing the wakeup events.

First, list all components and their current wakeup status:

$ cat /proc/acpi/wakeup
Device	S-state	  Status   Sysfs node
GP12	  S4	*enabled   pci:0000:00:07.1
GP13	  S4	*disabled  pci:0000:00:08.1
XHC0	  S4	*enabled   pci:0000:0a:00.3
GP30	  S4	*disabled
....
PT27	  S4	*disabled
PT28	  S4	*disabled
PT29	  S4	*disabled  pci:0000:03:09.0

You can temporarily toggle a device by writing its "Device" name back into /proc/acpi/wakeup

echo GPP0 | sudo tee /proc/acpi/wakeup

After finding out which component is causing unwanted wakeups you can use the sysfs id to find out the "vendor" and "device" fields:

$ cat /sys/class/pci_bus/0000:04/device/0000:04:00.0/vendor
0x1987
$ cat /sys/class/pci_bus/0000:04/device/0000:04:00.0/device
0x5013

And finally use those values in a udev rule:

services.udev.extraRules = ''
  ACTION=="add" SUBSYSTEM=="pci" ATTR{vendor}=="0x1987" ATTR{device}=="0x5013" ATTR{power/wakeup}="disabled"
'';

See also

External resources