14.11.电源和资源管理

以一种有效的方式利用硬件资源是很重要的。电源和资源管理允许操作系统监控系统极限,并可在系统温度意外升高时提供警报。提供电源管理的一个早期规范是高级电源管理(APM)设施。APM 根据系统的活动来控制其电源的使用。然而,对于操作系统来说,管理系统的电源使用和温度属性是困难的,也不灵活。硬件是由 BIOS 管理的,用户对电源管理设置的可配置性和可见性有限。APMBIOS 是由供应商提供的,是针对特定硬件平台的。操作系统中的 APM 驱动程序提供了对 APM 的软件访问接口,通过该接口可对功率水平进行管理。

APM 有四个主要的问题:首先,电源管理是由供应商特定的 BIOS 来完成,与操作系统分离。例如,用户可以在 APMBIOS 中为硬盘设置空闲时间值,这样一来,如果超过了空闲时间,BIOS 就会在未经操作系统同意的情况下将硬盘转速降低。第二,APM 逻辑被嵌入到 BIOS 中,它在操作系统的范围之外运行。这意味着用户只能通过将新的 APMBIOS 刷入 ROM 来解决 APMBIOS 的问题,这是一个危险的程序,一旦失败,有可能使系统处于无法恢复的状态。第三,APM 是一项供应商的特有技术,这意味着存在很多重复的工作,在一个供应商的 BIOS 中发现的错误可能无法在其他供应商中得到解决。最后,APMBIOS 没有足够的空间来实现复杂的电源策略或能够很好地适应机器的用途。

在大多数情况下即插即用 BIOS(PNPBIOS)都不可靠。PNPBIOS 是 16 位的技术,所以操作系统必须使用 16 位的仿真技术才能与 PNPBIOS 方法对接。FreeBSD 提供了 APM 驱动程序,因为在 2000 年或之前制造的系统仍会使用 APM。这个驱动在 apm(4)open in new window 中做了说明。

APM 的后续版本是高级配置与电源接口(ACPI)。ACPI 是一套由供应商联盟编写的标准,为硬件资源和电源管理提供一个接口。它是 在操作系统指导下的配置和电源管理 的一个关键因素,因为它为操作系统提供了更多的控制方式和灵活性。

本章演示了如何在 FreeBSD 上配置 ACPI。然后,它提供了一些关于如何调试 ACPI 的提示,以及如何提交包含调试信息的问题报告,以便开发人员能够诊断和修复 ACPI 问题。

14.11.1.配置 ACPI

在 FreeBSD 中,驱动 acpi(4)open in new window 在系统启动时默认加载, 应该被编译到内核中。这个驱动在启动后不能被卸载,因为系统总线使用它进行各种硬件交互。然而,如果系统遇到问题,可以通过在 /boot/loader.conf 中设置 hint.acpi.0.disabled="1" 后重启,或者通过在引导器提示下设置此变量来完全禁用 ACPI,如“第三阶段”open in new window所述。

注意

ACPI 和 APM 不能共存,应该分开使用。如果驱动程序注意到另一个正在运行,最后加载的那个将停止运行。

ACPI 可以用 acpiconf-s 标志和 1 到 5 的数字来使系统进入睡眠模式。大多数用户只需要 1(快速挂起到内存)或 3(挂起到内存)。选项 5 执行软关机,与运行 halt -p 相同。

驱动程序 acpi_video(4)open in new window 使用 ACPI 视频扩展open in new window来控制屏幕切换和背光亮度。它必须在任何 DRM 内核模块之后加载。在加载该驱动后,使用 Fn 亮度键可改变屏幕的亮度。可以通过检查 /var/run/devd.pipe 来查看 ACPI 事件:

...
# cat /var/run/devd.pipe
!system=ACPI subsystem=Video type=brightness notify=62
!system=ACPI subsystem=Video type=brightness notify=63
!system=ACPI subsystem=Video type=brightness notify=64
...

其他选项可以使用 sysctl 查看。更多信息请参考 acpi(4)open in new windowacpiconf(8)open in new window 的手册。

14.11.2.常见问题

ACPI 存在于所有符合 ia32(x86)和 amd64(AMD)架构的现代计算机中。完整的标准有许多功能,包括 CPU 性能管理、电源层控制、热管理、各种电池系统、嵌入式控制器和总线枚举。大多数系统实现的功能比完整标准要少。例如,台式机系统通常只实现总线枚举,而笔记本电脑可能也有冷却和电池管理支持。笔记本电脑还有挂起和恢复功能,并有其相关的复杂性。

一个符合 ACPI 标准的系统有各种组件。BIOS 和芯片组供应商在内存中提供各种固定表,如 FADT,指定 APIC 映射(用于 SMP)、配置寄存器和简单的配置值等。此外,字节码表,即差异化系统描述表 DSDT,指定了一个树状的设备和方法的名称空间。

ACPI 驱动程序必须解析这些固定的表格,实现字节码的解释器,并修改设备驱动程序和内核以接受来自 ACPI 子系统的信息。对于 FreeBSD,Intel® 提供了一个解释器(ACPI-CA),与 Linux® 和 NetBSD 共享。ACPI-CA 源代码的路径是 src/sys/contrib/dev/acpica。允许 ACPI-CA 在 FreeBSD 上工作的胶水代码在 src/sys/dev/acpica/Osd。最后,实现各种 ACPI 设备的驱动可在 src/sys/dev/acpica 中找到。

要使 ACPI 正确工作,所有的部分都必须正确工作。下面是一些常见的问题,按照出现的频率排列,以及一些可能的解决方法或修复方法。如果修复方法不能解决问题,请参阅“获取和提交调试信息”open in new window以了解如何提交错误报告的说明。

14.11.2.1.鼠标问题

在某些情况下,从休眠操作中恢复会导致鼠标失败。一个已知的解决方法是在 /boot/loader.conf 中添加 hint.psm.0.flags="0x3000"

14.11.2.2.休眠/恢复

ACPI 有三种休眠到内存(STR)的状态,S1-S3,和一种休眠到磁盘的状态(STD),称为 S4。STD 可以用两种不同的方式实现。S4BIOS 是一个由 BIOS 协助的挂起到磁盘的状态,S4OS 则完全由操作系统实现。当插上电源但不开机时,系统所处的正常状态是“软关机”(S5)。

使用 sysctl hw.acpi 可检查与挂起有关的项目。这些例子的结果来自 Thinkpad:

hw.acpi.supported_sleep_state: S3 S4 S5
hw.acpi.s4bios: 0

使用 acpiconf -s 来测试 S3S4S5s4bios 为一(1)表示 S4BIOS 的支持而不是 S4 操作系统的支持。

当测试休眠/恢复时,如果支持,从 S1 开始。这个状态最有可能工作,因为它不需要太多的驱动支持。没有人实现 S2,它与 S1 类似。接下来,尝试 S3。这是最深的 STR 状态,需要大量的驱动支持来正确地重新初始化硬件。

休眠/恢复的一个常见问题是,许多设备驱动程序不能正确地保存、恢复或重新初始化其固件、寄存器或设备内存。作为调试问题的第一次尝试,请尝试:

# sysctl debug.bootverbose=1
# sysctl debug.acpi.suspend_bounce=1
# acpiconf -s 3

这个测试模拟了所有设备驱动程序的休眠/恢复周期,而没有实际进入 S3 状态。在某些情况下,诸如丢失固件状态、设备看门狗超时、永远重试等问题,都可以用这种方法捕捉到。注意,系统不会真正进入 S3 状态,这意味着设备可能不会失去电源,即使休眠/恢复方法完全消失,许多设备也能正常工作,这与真正的 S3 状态不同。

如果前面的测试有效,在笔记本电脑上可以配置系统在关闭盖子时休眠进入 S3,并在再次打开时恢复:

# sysctl hw.acpi.lid_switch_state=S3

这一变化可以在重启后持续进行:

# echo 'hw.acpi.lid_switch_state=S3' >> /etc/sysctl.conf

更困难的情况需要额外的硬件,例如通过串行控制台进行调试的串行端口和电缆,使用 dcons(4)open in new window 的火线端口和电缆,以及内核调试技能。

为了帮助分离故障,应尽可能多地卸载驱动程序。如果它能工作,通过加载驱动程序来缩小问题所在,直到它再次失败。通常,像 nvidia.ko 这样的二进制驱动、显卡驱动和 USB 最有可能出现问题,而以太网接口通常工作良好。如果驱动程序可以被正确加载和卸载,可以通过在 /etc/rc.suspend/etc/rc.resume 中添加适当的命令来实现自动化。如果恢复后显示混乱,试着将 hw.acpi.reset_video 设置为 1。尝试为 hw.acpi.sleep_delay 设置更长或更短的值,看看是否有帮助。

试着使用一个最新的 Linux® 发行版,看看休眠/恢复在相同的硬件上是否工作。如果它在 Linux® 上有效,那就很可能是 FreeBSD 的驱动问题。缩小哪个驱动导致问题的范围将有助于开发人员解决这个问题。由于 ACPI 的维护者很少维护其他的驱动,例如声音或 ATA,任何驱动问题都应该发布到 FreeBSD-CURRENT 邮件列表open in new window中并邮寄给驱动维护者。专业用户可以在有问题的驱动程序中加入调试 printf(3)open in new window 的功能,以追踪它在恢复功能的哪个地方挂掉。

最后,尝试禁用 ACPI,启用 APM。如果休眠/恢复功能在 APM 下有效,那就坚持使用 APM,特别是在旧硬件上(2000 年以前)。供应商需要花时间来纠正对 ACPI 的支持,而且旧硬件更有可能在 ACPI 上出现 BIOS 问题。

14.11.2.3.系统挂起

大多数系统挂起是由于中断丢失或中断风暴造成的。芯片组可能存在问题,具体取决于引导、BIOS 如何在 APIC (MADT) 表正确之前配置中断以及系统控制中断 (SCI) 的路由。

中断风暴可以通过检查 vmstat -i 的输出并查看有 acpi0 的那一行来区别于丢失的中断。如果计数器的增加速度超过每秒几次,就有一个中断风暴。如果系统出现挂起的情况,尝试中断到 DDB(在控制台键入 CTRL + ALT + ESC)并输入 show interrupts

当处理中断问题时,尝试在 /boot/loader.conf 中用 hint.apic.0.disabled="1" 禁用 APIC 支持。

14.11.2.4. Panic

Panic 对于 ACPI 来说是比较少见的,而且是最优先修复的。第一步是隔离再现 panic 的步骤,如果可能的话,并得到回溯信息。按照“从串行线进入 DDB 调试器”open in new window选项 DDB 和设置串行控制台的建议,或者设置一个转储分区。要在 DDB 中获得回溯信息,使用 tr。当手动记录回溯信息时,至少要获得回溯信息中的前五行和最后五行。

然后,尝试通过禁用 ACPI 启动来分离故障。如果成功了,通过使用 debug.acpi.disable 的不同值来隔离 ACPI 子系统。参见 acpi(4)open in new window 的命令手册以了解一些例子。

14.11.2.5.系统在暂停和关机之后启动

首先,尝试在 /boot/loader.conf 中设置 hw.acpi.disable_on_poweroff="0"。这可以防止 ACPI 在关机过程中禁用各种事件,一些系统出于同样的原因需要将这个值设置为 1(默认值)。这通常可以解决系统在挂起或关机后自发开机的问题。

14.11.2.6. BIOS 含有带 bug 的字节码

一些 BIOS 供应商提供了不正确或有错误的字节码。这通常表现为像这样的内核控制台信息:

ACPI-1287: *** Error: Method execution failed [\\_SB_.PCI0.LPC0.FIGD._STA] \\
(Node 0xc3f6d160), AE_NOT_FOUND

通常,这些问题可以通过把 BIOS 更新到最新版本来解决。大多数控制台信息是无害的,但如果有其他问题,如电池状态不工作,这些信息是开始寻找问题的一个好地方。

14.11.3.覆盖默认的 AML

被称为 ACPI 机器语言(AML)的 BIOS 字节码是由称为 ACPI 源语言(ASL)的源语言编译而成。AML 可以在被称为差异化系统描述表(DSDT)的表格中找到。

FreeBSD 的目标是让每个人都有正常运行的 ACPI,而不需要任何用户干预。对于 BIOS 供应商所犯的常见错误,目前仍在开发解决方法。Microsoft® 解释器(acpi.sysacpiec.sys)并不严格检查 AML 是否符合标准,因此许多只在 Windows® 下测试 ACPI 的 BIOS 供应商从未修复他们的 ASL。FreeBSD 的开发人员继续识别并记录哪些非标准的行为是 Microsoft® 的解释器所允许的,并对其进行复制,这样 FreeBSD 就可以在不强迫用户修正 ASL 的情况下工作。

为了帮助识别有缺陷的行为,并有可能进行手动修复,可以对系统的 ASL 进行复制。要把系统的 ASL 复制到一个指定的文件名下,使用 acpidump -t,显示固定表的内容,和-d,来反汇编 AML:

# acpidump -td > my.asl

一些 AML 版本假设用户运行的是 Windows®。要覆盖这一点,在 /boot/loader.conf 中设置 hw.acpi.osname="Windows 2009",可使用 ASL 中列出的最新的 Windows® 版本。

其他解决方法可能需要定制 my.asl。如果这个文件被编辑了,用下面的命令编译新的 ASL。警告通常可以被忽略,但错误是 bug,通常会妨碍 ACPI 的正常工作。

# iasl -f my.asl

包含的 -f 将强制创建 AML,即使在编译过程中出现了错误。一些错误,例如缺少返回语句,会由 FreeBSD 解释器自动解决。

iasl 的默认输出文件名是 DSDT.aml。应通过编辑 /boot/loader.conf 来加载这个文件,而非 BIOS 的错误副本,后者仍然存在于存储器中,如下所示:

acpi_dsdt_load="YES"
acpi_dsdt_name="/boot/DSDT.aml"

请确保将 DSDT.aml 复制到 /boot,然后重新启动系统。如果这能解决这个问题,请将新旧 ASL 的 diff(1) 发送到 FreeBSD ACPI 邮件列表open in new window,以便开发人员能够解决 acpica 中的错误行为。

14.11.4.获得并提交调试信息

ACPI 驱动程序有一个灵活的调试工具。可以指定一组子系统和粗略的程度。调试的子系统被指定为层,并被分解为组件(ACPI_ALL_COMPONENTS)和 ACPI 硬件支持(ACPI_ALL_DRIVERS)。调试输出的粗略程度被指定为级别,范围从仅仅报告错误(ACPI_LV_ERROR)到全部(ACPI_LV_VERBOSE)。级别是一个位掩码,所以可以同时设置多个选项,用空格隔开。在实践中,应该用一个串行控制台来记录输出,这样它就不会因为控制台消息缓冲区的刷新而丢失。在 acpi(4)open in new window 中可以找到各个层和级别的完整列表。

调试输出在默认情况下是不启用的。要启用它,如果 ACPI 被编译到内核中,请在定制内核配置文件中添加 options ACPI_DEBUG。在 /etc/make.conf 中添加 ACPI_DEBUG=1 来全局启用它。如果使用模块而不是定制内核,只需重新编译 acpi.ko 模块,如下所示:

# cd /sys/modules/acpi/acpi && make clean && make ACPI_DEBUG=1

将编译好的 acpi.ko 复制到 /boot/kernel,并在 /boot/loader.conf 中添加所需的级别和层次。本例中的条目启用了所有 ACPI 组件和硬件驱动的调试信息,并以最简洁的级别输出错误信息:

debug.acpi.layer="ACPI_ALL_COMPONENTS ACPI_ALL_DRIVERS"
debug.acpi.level="ACPI_LV_ERROR"

如果所需的信息是由特定的事件触发的,比如挂起后再恢复,不要修改 /boot/loader.conf。而应该在启动并为特定事件准备好系统后,使用 sysctl 来指定层和级别。使用 sysctl 设置的变量与 /boot/loader.conf 中的参数项命名相同。

如果收集到了调试信息,就可以将其发送到 FreeBSD ACPI 邮件列表open in new window中,以便 FreeBSD ACPI 维护者使用这些信息来确定问题的根本原因并制定解决方案。

注意

在向该邮件列表提交调试信息之前,请确保安装了最新版本的 BIOS,如果有的话,也请更新嵌入式控制器固件版本。

当提交错误报告时,请包含以下信息:

  • 对错误行为的摘要,包括系统类型、型号以及导致错误出现的任何东西。如果是新发生的,尽可能准确地记下该 bug 开始出现的时间。

  • 运行 boot -vdmesg 的输出,包括由错误产生的任何错误信息。

  • 如果禁用 ACPI 有助于解决这个问题的话,那么请包含在禁用 ACPI 的情况下,得到 boot -vdmesg 的输出。

  • sysctl hw.acpi 的输出,它列出了系统提供的功能。

  • 系统 ASL 的复制版本的网址链接。不要 直接将 ASL 发送到列表中,因为它可能非常大。通过运行这个命令生成一份 ASL 的副本:

    # acpidump -dt > name-system.asl
    

    用登录名代替 姓名,用制造商/型号代替 系统。例如,使用 njl-FooCo6000.asl

大多数 FreeBSD 开发者会关注 FreeBSD-CURRENT 邮件列表open in new window,但也应该把问题提交到 FreeBSD ACPI 邮件列表open in new window,以确保它被看到。在等待回复时要有耐心。如果问题没有立即显现出来,请提交一份错误报告。当输入 PR 时,请包括与上述要求相同的信息。这有助于开发人员跟踪问题并解决它。不要在没有给 FreeBSD ACPI 邮件列表open in new window发邮件的情况下发送 PR,因为这个问题很可能已经被报告过了。

14.11.5.参考资料

你可以在以下地点找到更多关于 ACPI 的信息: