电源管理/唤醒触发器

来自 Arch Linux 中文维基

唤醒触发器是可以将系统从任何硬件节能状态唤醒的事件源。显而易见的例子是电源或挂起按钮、网络唤醒英语Wake-on-LAN功能或笔记本电脑系统中的盖开关。唤醒触发器可以通过下面列出的各种内核接口进行控制。没有涵盖所有可能的触发器的统一接口。

唤醒触发器接口[编辑 | 编辑源代码]

/proc/acpi/wakeup[编辑 | 编辑源代码]

读取 /proc/acpi/wakeup 文件将生成 ACPI 注册的唤醒源列表,其中包含相应的 sysfs IDs(如果可用)。将条目从设备列写入文件可切换其状态。例如,要禁用打开笔记本电脑盖时的唤醒功能,请运行:

# echo "LID" > /proc/acpi/wakeup

/sys/module/acpi/parameters/ec_no_wakeup[编辑 | 编辑源代码]

该文件表示 ACPI 内核模块选项 ec_no_wakeup 的值,当系统处于挂起到空闲(s2idle电源状态时,该选项控制从嵌入式控制器传递唤醒触发器[1]。在现代笔记本电脑上,嵌入式控制器唤醒在某些英语Lenovo ThinkPad Helix 2nd Gen#Disable embedded-controller wake-ups情况下会导致电池过度消耗。

/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_wakeups 内核参数,而 udev事件驱动 途径是配置 sysfs 设备的可靠方式。

使用 systemd 的原先方法[编辑 | 编辑源代码]

ec_no_wakeups ACPI 内核模块选项可以在启动时设置,如本文所述。在引导时设置 sysfs 值的标准解决方案是 systemd services,例如在故障排除中。/proc/acpi/wakeup 的另一个基于 systemd 的管理器是 wakeup-triggersAUR

一些系统可以在电源状态转换时覆盖一些 ACPI 唤醒触发器,这更像是一个 bug,而不是一个功能。如果硬件能在可预测的时间覆盖触发器,则仍然可以使用适当构建的 systemd units 来解决。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 中找到的特定设备的可能参数。

这里一个典型的常见例子是罗技统一 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

挂起后被立即唤醒[编辑 | 编辑源代码]

对于某些带有 LynxPoint 或 LynxPoint-LP 芯片组的 Intel Haswell 系统,有报告称它们会在挂起后就立即被唤醒。这与错误的 BIOS ACPI 实现,以及 xhci_hcd 如何在引导期间如何解释它们都有关。作为一项解决方案,内核会将报告的受影响系统逐个添加到拒绝列表(名为 XHCI_SPURIOUS_WAKEUP[3]

电脑也有可能在其它情况下发生立即恢复,比如,一个 USB 设备在挂起后插入,且启动了 ACPI 唤醒触发器。对于这样的情况,如果受影响的系统尚未被加入上述拒绝列表,一个可行的方案是禁用相关的唤醒触发器。通过 USB 禁用唤醒的例子见后[4]

要查看当前配置:

$ 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#临时文件论坛帖子以了解可能的解决方案。

nouveau 驱动程序[编辑 | 编辑源代码]

如果使用了 nouveau 驱动程序,则立即唤醒的原因可能是驱动程序中的错误,这有时会阻止 GPU 挂起。一种可能的解决方法是在休眠前卸载 nouveau 内核模块,并在唤醒后重新加载。为此,请创建以下脚本:

/usr/lib/systemd/system-sleep/10-nouveau.sh
#!/bin/bash

case $1/$2 in
  pre/*)
    # echo "进入 $2 状态..."
    /usr/bin/echo "0" > /sys/class/vtconsole/vtcon1/bind
    /usr/bin/rmmod nouveau
    ;;
  post/*)
    # echo "从 $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 其中哪一个是 frame buffer device [5]