Power Management: Difference between revisions
imported>Samueldr Created page with "{{expansion}} == Suspend == === Suspend hooks === NixOS provides the {{nixos:option|powerManagement.resumeCommands}} option which defines commands that are added to a globa..." |
|||
| (17 intermediate revisions by 8 users not shown) | |||
| Line 1: | Line 1: | ||
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 <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 | It is also possible to use the <code>post-resume</code> target directly to make a service. | ||
<syntaxhighlight lang="nix"> | <syntaxhighlight lang="nix"> | ||
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." | echo "This should show up in the journal after resuming." | ||
''; | |||
serviceConfig.Type = "oneshot"; | |||
}; | }; | ||
</syntaxhighlight> | |||
=== 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. | |||
Please note that <code>resumeDevice</code> must match the output of <code>swapon -s</code> especially if you're dealing with mapped volumes (LUKS, logical volumes, logical volumes under LUKS, etc.). If you're using a swapfile, you must also [https://search.nixos.org/options?channel=unstable&show=boot.resumeDevice&from=0&size=50&sort=relevance&type=packages&query=resume+offset specify the offset to it.] | |||
Therefore, an example configuration could look like this:<syntaxhighlight lang="nix"> | |||
// I'm hibernating into a logical volume that's also under LUKS. Pretty cool, right? | |||
swapDevices = [ | |||
{ | |||
device = "/dev/VG/SWAP"; | |||
} | |||
]; | |||
boot.resumeDevice = "/dev/dm-7"; | |||
</syntaxhighlight>Derived from a system with the following output from <code>swapon -s</code> :<syntaxhighlight lang="text">Filename Type Size Used Priority | |||
/dev/dm-7 partition 67108860 00 | |||
/dev/zram0 partition 32881148 032767</syntaxhighlight> | |||
Test and use hibernation with the following command:<syntaxhighlight lang="nix"> | |||
systemctl hibernate | |||
</syntaxhighlight> | |||
== Tips and tricks == | |||
=== 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> | |||
Or, to disable suspend entirely, consider a configuration like this: | |||
<syntaxhighlight lang="nix"> | |||
systemd.sleep.extraConfig = '' | |||
AllowSuspend=no | |||
AllowHibernation=no | |||
AllowHybridSleep=no | |||
AllowSuspendThenHibernate=no | |||
''; | |||
</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: | |||
==== 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. | |||
<syntaxhighlight lang="nix"> | |||
services.udev.extraRules = '' | |||
ACTION=="add", SUBSYSTEM=="pci", DRIVER=="pcieport", ATTR{power/wakeup}="disabled" | |||
''; | |||
</syntaxhighlight> | |||
==== Solution 2: Disable a common NVMe interface ==== | |||
Specifically on Gigabyte motherboards you can try targetting only the NVMe ports. | |||
<syntaxhighlight lang="nix"> | |||
services.udev.extraRules = '' | |||
ACTION=="add" SUBSYSTEM=="pci" ATTR{vendor}=="0x1022" ATTR{device}=="0x1483" ATTR{power/wakeup}="disabled" | |||
''; | |||
</syntaxhighlight> | |||
==== 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: | |||
<syntaxhighlight lang="shell-session"> | |||
$ 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 | |||
</syntaxhighlight> | |||
You can temporarily toggle a device by writing its "Device" name back into <code>/proc/acpi/wakeup</code> | |||
<syntaxhighlight lang="sh"> | |||
echo GPP0 | sudo tee /proc/acpi/wakeup | |||
</syntaxhighlight> | |||
After finding out which component is causing unwanted wakeups you can use the sysfs id to find out the "vendor" and "device" fields: | |||
<syntaxhighlight lang="console"> | |||
$ 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 | |||
</syntaxhighlight> | |||
And finally use those values in a <code>udev</code> rule: | |||
<syntaxhighlight lang="nix"> | |||
services.udev.extraRules = '' | |||
ACTION=="add" SUBSYSTEM=="pci" ATTR{vendor}=="0x1987" ATTR{device}=="0x5013" ATTR{power/wakeup}="disabled" | |||
''; | |||
</syntaxhighlight> | |||
=== Suspend blocked by <code>pre-sleep.service</code> === | |||
Sometimes, the system appears to suspend (Wi-Fi turns off, screen locks), but the hardware does not actually suspend, and all subsequent <code>systemctl suspend</code> or <code>systemctl reboot</code> commands are met with: | |||
<syntaxhighlight lang="console"> | |||
# systemctl suspend | |||
Call to Suspend failed: Action suspend already in progress, refusing requested suspend operation. | |||
# systemctl reboot | |||
Call to Reboot failed: Action suspend already in progress, refusing requested reboot operation. | |||
</syntaxhighlight> | |||
If directly telling the kernel to suspend as root works: | |||
<syntaxhighlight lang="console"> | |||
# echo mem > /sys/power/state | |||
</syntaxhighlight> | |||
Then a long-running <code>pre-sleep.service</code> might be hanging the sleep. This can be verified with <code>systemctl list-jobs</code>: | |||
<syntaxhighlight lang="console"> | |||
# systemctl list-jobs | |||
JOB UNIT TYPE STATE | |||
12144 suspend.target start waiting | |||
12149 pre-sleep.service start running | |||
12145 systemd-suspend.service start waiting | |||
12268 post-resume.target start waiting | |||
12148 sleep.target start waiting | |||
12269 post-resume.service start waiting | |||
</syntaxhighlight> | |||
Here, the <code>pre-sleep.service</code> is blocking and halting suspend. To see why, we can use <code>systemctl cat pre-sleep.service</code>: | |||
<syntaxhighlight lang="systemd"> | |||
# systemctl cat pre-sleep.service | |||
# /etc/systemd/system/pre-sleep.service | |||
[Unit] | |||
Before=sleep.target | |||
Description=Pre-Sleep Actions | |||
[Service] | |||
# <... Omitted Environment directives PATH, LOCALE_ARCHIVE, TZDIR ...> | |||
ExecStart=/nix/store/yzf7cpiqzq49san2frijxsh160zjy6fp-unit-script-pre-sleep-start/bin/pre-sleep-start | |||
Type=oneshot | |||
[Install] | |||
WantedBy=sleep.target | |||
</syntaxhighlight> | </syntaxhighlight> | ||
In this case, the <code>pre-sleep-start</code> script referenced by <code>ExecStart</code> contained directives installed by the [[Displaylink]] package, that contained a flush operation which hung the suspend action. Starting <code>dlm.service</code> or running <code>sudo DisplayLinkManager</code> unblocks the script and made suspend work normally. | |||
==== Cancelling an existing suspend action ==== | |||
An existing suspend operation that is hung may be interrupted using <code>'''systemctl cancel'''</code> in case reboots or internet access is needed. | |||
== 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}} | ||
[[Category:Configuration]] | |||