Power management/Wakeup triggers
Wakeup triggers are event sources that can wake the system from any of the hardware power-saving states. The obvious example is the power or suspend button, the Wake-on-LAN functionality or the lid switch in laptop systems. Wakeup triggers can be controlled through various kernel interfaces listed below. There is no unified interface covering all possible triggers.
Wakeup trigger interfaces
/proc/acpi/wakeup
Reading the /proc/acpi/wakeup
file yields list of ACPI-registered wakeup sources with corresponding sysfs
IDs where available. Writing an entry from the Device column to the file toggles its state. For example, to disable waking on opening the laptop lid, run:
# echo "LID" > /proc/acpi/wakeup
/sys/module/acpi/parameters/ec_no_wakeup
This file represents the value of ACPI kernel module option ec_no_wakeup
, which controls passing the wakeup triggers from embedded controller when the system is in the suspend-to-idle (s2idle
) power state [1]. On modern laptops embedded controller wakeups can cause excessive battery drain in some cases.
/sys/devices/
Each sysfs
device that supports wakeup contains the file wakeup
in a device's power
subdirectory. The file contains wakeup trigger's status and can be written to as well. Bus controllers as well as endpoint devices can be capable of waking up the system. For example, to disable wakeups from the first USB controller (bus), run:
# echo "disabled" > /sys/bus/usb/devices/usb1/power/wakeup
An endpoint device should be able to wake the device if the trigger is enabled regardless of the controller's setting, however this might be hardware-dependent.
Program PowerTOP interfaces with sysfs
, but it only lists wakeup triggers of networking and USB devices by reading /sys/class/net/
and /sys/bus/usb/devices/
(containing symlinks to /sys/devices/
).
/sys/class/wakeup/*
Almost all wakeup triggers can be found in the /sys/class/wakeup
directory, which contains symlinks to all relevant devices. This is useful for finding possible wakeup triggers by going through subdirectories. Some of the triggers can correspond to virtual devices while hardware-related wakeup triggers are the ones that contain at least one of these files:
/sys/class/wakeup/*/device/physical_node/power/wakeup /sys/class/wakeup/*/device/power/wakeup
Some of wakeup triggers in /sys/class/wakeup
also provide a link to the cryptic /proc/acpi/wakeup
names where the following file is present:
/sys/class/wakeup/*/device/path
Persistent settings
The one-time methods should suffice for setting the /proc/acpi/wakeup
states and acpi.ec_no_wakeups
kernel parameter while the event-driven approach with udev is the reliable way to configure the sysfs
devices.
One-time with systemd
The ec_no_wakeups
ACPI kernel module option can be set at boot as described in the article. The standard solution to set the sysfs
values at boot are systemd services such as in this troubleshooting case. Another systemd-based manager for /proc/acpi/wakeup
is wakeup-triggersAUR.
Some systems can override some of the ACPI wakeup triggers upon power state transition(s) in what is more of a bug rather than a feature. If the hardware is overriding triggers at predictable times that can still be solved with appropriately crafted systemd units. The sleep.target
is a generic target covering all different suspended states that might be helpful in this case, but there is no generic wakeup.target
[2].
This method only works reliably with sysfs
devices that are connected all the time.
Event-driven with udev
Setting the wakeup trigger status with udev rules is an event-driven method that works reliably any time the devices with wakeup triggers are connected. The key is to detect an addition of a new device (ACTION=="add"
) in a rule and set the wakeup trigger status with ATTR{power/wakeup}="disabled"
. If the hardware is resetting this setting, udev can try to circumvent it by reapplying rules upon every device change (ACTION=="add|change"
). A device tree with possible parameters for matching a particular device found in sysfs
can be obtained with udevadm info -q all -a /sys/devices/...
.
A representative common example here would be a Logitech Unifying USB receiver. Its wakeup trigger should be enabled by default and if that is not desired, a solution could be an udev rule, as follows:
/etc/udev/rules.d/logitech-unifying.rules
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", ATTR{power/wakeup}="disabled"
The reverse case to enable the necessary trigger(s) is described in the udev article.
udev triggers so early in the device enumeration that disabling wakeup trigger with the method above causes (some?) disabled triggers to not be listed in /sys/class/wakeup
. That might be dependent on whether the device was already present at boot and needs further clarification.
Troubleshooting
List device and/or bus trees
These auxiliary commands can be helpful when trying to understand all wakeup triggers of a certain system, to aid with udev rule writing or general wakeup source troubleshooting:
# lshw -businfo -numeric # lspci -DPPnn # lsusb -tvv
Last wakeup trigger source
The information on which wakeup source was the reason for the last device wakeup is unfortunately platform-specific. On x86 machines dmidecode can be used:
# dmidecode -t system | grep -P '\tWake-up Type\: '
Instantaneous wakeup after suspending
Intel Haswell with LynxPoint(-LP)
For some Intel Haswell systems with the LynxPoint and LynxPoint-LP chipset, instantaneous wakeups after suspending are reported. They are linked to erroneous BIOS ACPI implementations and how the xhci_hcd
module interprets it during boot. As a work-around reported affected systems are added to a denylist (named XHCI_SPURIOUS_WAKEUP
) by the kernel case-by-case [3].
Wakeup may happen, for example, if a USB device is plugged during suspending and ACPI wakeup triggers are enabled. A viable workaround for such a system is to disable the relevant wakeup triggers. An example to disable wakeup through USB is described as follows [4].
To view the current configuration:
$ cat /proc/acpi/wakeup
Device S-state Status Sysfs node ... EHC1 S3 *enabled pci:0000:00:1d.0 EHC2 S3 *enabled pci:0000:00:1a.0 XHC S3 *enabled pci:0000:00:14.0 ...
The relevant devices are EHC1
, EHC2
and XHC
(for USB 3.0). To toggle their state you have to echo the device name to the file as root:
# echo EHC1 > /proc/acpi/wakeup # echo EHC2 > /proc/acpi/wakeup # echo XHC > /proc/acpi/wakeup
This should result in suspension working again. However, this settings are only temporary and need to be set at every boot. To automate this, see systemd-tmpfiles or BBS thread for possible solutions.
Gigabyte motherboards
On some Gigabyte motherboards, the GPP bridge to the NVMe drive drive may cause premature wakeups from suspend.
Known boards affected:
- B550i AORUS,
- B550 AORUS ELITE V2,
- B550 AORUS ELITE AX V2 (Rev. 1.5)
Setting the status of GPP0
to disabled may fix the issue:
# echo GPP0 > /proc/acpi/wakeup
Same as the Haswell solution above, this setting is only temporary. An example of automating the fix can be found in this BBS thread.
NVIDIA drivers
Installing NVIDIA proprietary drivers might render suspension and hibernation not possible. In that case the system log might include:
kernel: NVRM: GPU 0000:01:00.0: PreserveVideoMemoryAllocations module parameter is set. System Power Management attempted without driver procfs suspend interface. Please refer to the 'Configuring Power Management Support' section in the driver README. kernel: PM: pci_pm_suspend(): nv_pmops_suspend+0x0/0x20 [nvidia] returns -5 kernel: PM: dpm_run_callback(): pci_pm_suspend+0x0/0x160 returns -5 kernel: nvidia 0000:01:00.0: PM: failed to suspend async: error -5
See NVIDIA/Tips and tricks#Preserve video memory after suspend.
nouveau driver
If the nouveau driver is used, the reason for instantaneous wakeups may be a bug in the driver, which sometimes prevents GPU from suspending. A possible workaround is unloading the nouveau
kernel module right before going to sleep and loading it back after wakeup. To do this, create the following script:
/usr/lib/systemd/system-sleep/10-nouveau.sh
#!/bin/bash case $1/$2 in pre/*) # echo "Going to $2..." /usr/bin/echo "0" > /sys/class/vtconsole/vtcon1/bind /usr/bin/rmmod nouveau ;; post/*) # echo "Waking up from $2..." /usr/bin/modprobe nouveau /usr/bin/echo "1" > /sys/class/vtconsole/vtcon1/bind ;; esac
The first echo line unbinds nouveaufb
from the framebuffer console driver (fbcon
). Usually it is vtcon1
as in this example, but it may also be another vtcon*
. See /sys/class/vtconsole/vtcon*/name
which one of them is a frame buffer device [5].