电源管理/唤醒触发器
唤醒触发器是事件源,可以使系统从任何硬件节能状态中唤醒。显而易见的例子是电源或暂停按钮、网络唤醒功能或笔记本电脑系统中的 lid 开关。唤醒触发器可以通过下面列出的各种内核接口进行控制。没有涵盖所有可能触发器的统一接口。
唤醒触发器接口
/proc/acpi/wakeup
读取 /proc/acpi/wakeup
文件会生成 ACPI 注册的唤醒源列表,并在可用时提供相应的 sysfs
ID。将设备列中的条目写入文件会切换其状态。例如,要禁用打开笔记本电脑盖子时的唤醒,请运行
# echo "LID" > /proc/acpi/wakeup
/sys/module/acpi/parameters/ec_no_wakeup
此文件表示 ACPI 内核模块选项 ec_no_wakeup
的值,该选项控制当系统处于暂停到空闲 (s2idle
) 电源状态[1]时,是否传递来自嵌入式控制器的唤醒触发器。在现代笔记本电脑上,嵌入式控制器唤醒可能会在某些情况下导致电池过度消耗。
/sys/devices/
每个支持唤醒的 sysfs
设备都在设备的 power
子目录中包含文件 wakeup
。该文件包含唤醒触发器的状态,也可以写入。总线控制器以及端点设备都可能能够唤醒系统。例如,要禁用来自第一个 USB 控制器(总线)的唤醒,请运行
# echo "disabled" > /sys/bus/usb/devices/usb1/power/wakeup
如果启用了触发器,则无论控制器的设置如何,端点设备都应该能够唤醒设备,但这可能取决于硬件。
程序 PowerTOP 与 sysfs
接口,但它仅通过读取 /sys/class/net/
和 /sys/bus/usb/devices/
(包含指向 /sys/devices/
的符号链接)来列出网络和 USB 设备的唤醒触发器。
/sys/class/wakeup/*
几乎所有唤醒触发器都可以在 /sys/class/wakeup
目录中找到,该目录包含指向所有相关设备的符号链接。这对于通过浏览子目录来查找可能的唤醒触发器非常有用。某些触发器可能对应于虚拟设备,而与硬件相关的唤醒触发器是至少包含以下文件之一的触发器
/sys/class/wakeup/*/device/physical_node/power/wakeup /sys/class/wakeup/*/device/power/wakeup
/sys/class/wakeup
中的某些唤醒触发器还提供了指向神秘的 /proc/acpi/wakeup
名称的链接,其中存在以下文件
/sys/class/wakeup/*/device/path
持久化设置
一次性方法应足以设置 /proc/acpi/wakeup
状态和 acpi.ec_no_wakeup
内核参数,而使用 udev 的事件驱动方法是配置 sysfs
设备的可靠方法。
使用 systemd 一次性设置
ec_no_wakeup
ACPI 内核模块选项可以在启动时设置,如文章中所述。在启动时设置 sysfs
值的标准解决方案是 systemd 服务,例如此故障排除案例中所示。另一个基于 systemd 的 /proc/acpi/wakeup
管理器是 wakeup-triggersAUR。
某些系统可能会在电源状态转换时覆盖某些 ACPI 唤醒触发器,这更像是一个错误而不是一个功能。如果硬件在可预测的时间覆盖触发器,仍然可以使用精心制作的 systemd 单元来解决。sleep.target
是一个通用目标,涵盖所有不同的暂停状态,可能在这种情况下有所帮助,但没有通用的 wakeup.target
[2]。
此方法仅适用于始终连接的 sysfs
设备。
使用 udev 事件驱动设置
使用 udev 规则设置唤醒触发器状态是一种事件驱动的方法,该方法在任何时候设备与唤醒触发器连接时都可靠地工作。关键是在规则中检测到新设备的添加 (ACTION=="add"
),并使用 ATTR{power/wakeup}="disabled"
设置唤醒触发器状态。如果硬件正在重置此设置,则 udev 可以通过在每次设备更改时重新应用规则 (ACTION=="add|change"
) 来尝试规避它。可以使用 udevadm info -q all -a /sys/devices/...
获取在 sysfs
中找到的用于匹配特定设备的设备树以及可能的参数。
这里一个有代表性的常见示例是罗技 Unifying USB 接收器。其唤醒触发器应默认启用,如果不需要,解决方案可以是 udev 规则,如下所示
/etc/udev/rules.d/logitech-unifying.rules
ACTION=="add", SUBSYSTEM=="usb", DRIVERS=="usb", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c52b", ATTR{power/wakeup}="disabled"
udev 文章中描述了启用必要触发器的相反情况。
udev 在设备枚举的早期触发,因此使用上述方法禁用唤醒触发器会导致(某些?)禁用的触发器不列在 /sys/class/wakeup
中。这可能取决于设备是否已在启动时存在,并且需要进一步澄清。
故障排除
列出设备和/或总线树
当尝试了解特定系统的所有唤醒触发器时,这些辅助命令可能很有用,以帮助编写 udev 规则或进行常规唤醒源故障排除
# lshw -businfo -numeric # lspci -DPPnn # lsusb -tvv
上次唤醒触发源
不幸的是,关于哪个唤醒源是上次设备唤醒原因的信息是平台特定的。在 x86 机器上,可以使用 dmidecode
# dmidecode -t system | grep -P '\tWake-up Type\: '
但是,对于某些计算机,它始终报告“电源开关”,而不管实际原因如何,例如,对于任何 USB 键盘、笔记本电脑键盘、电源开关和鼠标。
暂停后立即唤醒
带有 LynxPoint(-LP) 的 Intel Haswell
对于某些带有 LynxPoint 和 LynxPoint-LP 芯片组的 Intel Haswell 系统,报告了暂停后立即唤醒的情况。它们与错误的 BIOS ACPI 实现以及 xhci_hcd
模块在启动期间如何解释它有关。作为一种解决方法,受影响的系统会逐个添加到内核案例的拒绝列表(命名为 XHCI_SPURIOUS_WAKEUP
)中[3]。
例如,如果在暂停期间插入 USB 设备并且启用了 ACPI 唤醒触发器,则可能会发生唤醒。对于此类系统,一种可行的解决方法是禁用相关的唤醒触发器。以下链接[4]描述了禁用通过 USB 唤醒的示例。
要查看当前配置
$ 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 ...
相关设备是 EHC1
、EHC2
和 XHC
(用于 USB 3.0)。要切换它们的状态,您必须以 root 身份将设备名称回显到文件
# echo EHC1 > /proc/acpi/wakeup # echo EHC2 > /proc/acpi/wakeup # echo XHC > /proc/acpi/wakeup
这应该会导致暂停再次工作。但是,这些设置只是临时的,需要在每次启动时设置。要自动化此操作,请参阅 systemd-tmpfiles 或 BBS 线程,以获得可能的解决方案。
技嘉主板
GPP 桥
在某些技嘉主板上,到 NVMe 驱动器的 GPP 桥可能会导致从暂停状态过早唤醒。
已知的受影响主板
- B550i AORUS,
- B550 AORUS ELITE V2,
- B550 AORUS ELITE AX V2 (Rev. 1.5),
- B550M DS3H
- B550M AORUS PRO-P
将 GPP0
的状态设置为禁用可能会解决此问题
# echo GPP0 > /proc/acpi/wakeup
与上面的 Haswell 解决方案相同,此设置也只是临时的。可以在此 BBS 线程中找到自动化修复的示例。
ACPI _OSI 字符串
对于某些技嘉主板,禁用 /proc/acpi/wakeup
中的所有内容(包括 GPP0
)并不能阻止从暂停状态立即唤醒。如果是这种情况,您的主板可能在 Linux 中存在 问题,与 ACPI 相关。
已知的受影响主板
- B650 GAMING X AX V2
- B650i AORUS ULTRA (Rev. 1.0)
- X670E AORUS PRO X
将以下内容应用于您的内核参数
acpi_osi="!Windows 2015"
- 您也可以尝试 DSDT 提供的其他字符串。有关示例,请参见DSDT#告诉内核报告 Windows 版本。
- 根据您的设置,您可能需要或不需要禁用唤醒触发器。请参阅#唤醒触发器接口。
NVIDIA 驱动
安装 NVIDIA 专有驱动程序可能会使暂停和休眠变得不可能。在这种情况下,系统日志可能包含
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
nouveau 驱动
如果使用 nouveau 驱动程序,则瞬时唤醒的原因可能是驱动程序中的错误,有时会阻止 GPU 暂停。一种可能的解决方法是在进入睡眠状态之前卸载 nouveau
内核模块,并在唤醒后重新加载它。为此,请创建以下脚本
/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
第一行 echo 命令将 nouveaufb
从帧缓冲控制台驱动程序 (fbcon
) 中解绑。通常它如本例中所示是 vtcon1
,但也可能是另一个 vtcon*
。请参阅 /sys/class/vtconsole/vtcon*/name
,其中哪一个是帧缓冲设备[5]。