电源管理/唤醒触发器

来自 ArchWiki

唤醒触发器是事件源,可以使系统从任何硬件节能状态中唤醒。显而易见的例子是电源或暂停按钮、网络唤醒功能或笔记本电脑系统中的 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

如果启用了触发器,则无论控制器的设置如何,端点设备都应该能够唤醒设备,但这可能取决于硬件。

程序 PowerTOPsysfs 接口,但它仅通过读取 /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
...

相关设备是 EHC1EHC2XHC(用于 USB 3.0)。要切换它们的状态,您必须以 root 身份将设备名称回显到文件

# echo EHC1 > /proc/acpi/wakeup
# echo EHC2 > /proc/acpi/wakeup
# echo XHC > /proc/acpi/wakeup

这应该会导致暂停再次工作。但是,这些设置只是临时的,需要在每次启动时设置。要自动化此操作,请参阅 systemd-tmpfilesBBS 线程,以获得可能的解决方案。

技嘉主板

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"
注意

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

请参阅 NVIDIA/技巧和窍门#在暂停后保留视频内存

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]