11.13. 电源与资源管理

Written by Hiten PandyaTom Rhodes.

以有效率的方式运用硬件资源是很重要的,电源与资源管理让操作系统可以监控系统的限制,并且在系统温度意外升高时能够发出警报。早期提供电源管理的规范是进阶电源管理(Advanced Power Management,APM),APM可根据系统的使用状况来来控制电源用量。然而,使用APM要操作系统来管理系统的电源用量和温度属性是困难且没有弹性的,因为硬件是由BIOS所管理,使用者对电源管理设定只有有限的设定性与可见性,且APM BIOS是由供应商提供且特定于某些硬件平台,而操作系统中必透过APM驱动程序做为中介存取APM软件界面才能够管理电源等级。

APM有四个主要的问题。第一,电源管理是由供应商特定的BIOS来完成,与操作系统是分开的。例如,使用者可在APM BIOS设定硬盘的闲置时间值,在超过时间时BIOS可在未征得操作系统的同意下降低硬盘的转速。第二,APM的逻辑是内嵌在BIOS当中的,并且在操作系统范围之外运作,这代表使用者只能够透过刻录新的固件到ROM来修正APM BIOS中的问题,而这样的程序是危险的,若失败,可能会让系统进入无法复原的状态。第三,APM是供应商特定的技术,这代表有许多重复的工作,在一个供应商的BIOS找到的问题在其他的供应商却没有解决。最后一点,APMBIOS并没有足够的空间来实作复杂的电源管理政策或可良好调节主机用途的程序。

Plug and Play BIOSPNPBIOS)在很多情况下并不可靠,PNPBIOS是16位的技术,所以操作系统必须模拟16位才能存取PNPBIOS。FreeBSD提供了一个APM驱动程序来做APM,应可用在2000年之前所制造的系统,该驱动程序的说明于apm(4)

APM的后继者是进阶设置与电源界面(Advanced Configuration and Power Interface,ACPI)。ACPI是一套由供应商联盟所搛写出的标准,提供了硬件资源与电源管理的界面,它是操作系统直接设置与电源管理(Operating System-directed configuration and Power Management)关键的要素,提供了操作系统更多的控制方式与弹性。

本章节将示范如何在FreeBSD设定ACPI,然后提供一些如何对ACPI除错的提示以及如何提交包含除错信息的问题回报,让开发人员能够诊断并修正ACPI的问题。

11.13.1. 设定ACPI

在FreeBSD acpi(4)驱动程序预设会在系统开始时加载,且应被编译到核心当中。这个驱动程序在开机之后无法被卸载,因为系统总线会使用它做各种硬件互动。虽然如此,若系统遇到问题,ACPI还是可以被关闭,在/boot/loader.conf中设定hint.acpi.0.disabled=“1”之后重启或在加载程序提示时设定这个变数,如第 12.2.3 节 “阶段三”中的说明。

注意:

ACPIAPM不能同时存在且应分开使用,若有侦测到有另一个正在执行,要加载的后者将会中断。

ACPI可以用来让系统进入睡眠模式,使用acpiconf-s旗标再加上由15的数字。大多数使用者只需使用1(快速待命到RAM)或3(待命到RAM),选项5会执行软关机(Soft-off),如同执行halt -p一样。

其他的选项可使用sysctl来设定,请参考acpi(4)以及acpiconf(8 )以取得更多资讯。

11.13.2. 常见问题

所有符合 ia32 (x86) 和 amd64 (AMD) 体系结构的现代计算机都有ACPI。全标准具有多种功能,包括CPU性能管理、电源平面控制、热区、各种电池系统、嵌入式控制器和总线枚举。大多数系统实施不到完整的标准。例如,桌面系统通常只实现总线枚举,而笔记本电脑可能也有冷却和电池管理支持。笔记本电脑也有挂起和恢复,与它们相关的复杂性。

符合 ACPI 的系统中有许多组件。 BIOS 和芯片组制造商提供一些固定的表 (例如, FADT) 在存储器中, 以提供类似 APIC 映射 (用于 SMP)、 配置寄存器、 以及简单的配置值等等。 另外, 一个字节代码 (bytecode) 表 (系统区别描述表 DSDT) 则提供了通过树状命名空间来指定设备及其功能的方法。

ACPI 驱动必须要处理固定表, 实现字节码解释器, 并修改驱动程序和内核, 以接受来自 ACPI 子系统的信息。 对于 FreeBSD, Intel® 提供了一个解释器 (ACPI-CA), 它在 Linux 和 NetBSD 也可以使用。 ACPI-CA 源代码可以在 src/sys/contrib/dev/acpica 找到。 用于在 FreeBSD 中允许 ACPI-CA 正确运转的代码则在 src/sys/dev/acpica/Osd。 最后, 用于实现 ACPI 设备的驱动可以在 src/sys/dev/acpica 找到。

要让 ACPI 正常工作, 它的每一部分都必须工作正常。 下面是一些常见的问题, 按照出新的频繁程度排序, 并给出了一些绕过或修正它们的方法。如果无法解决问题,请按照第 11.13.4 节 “获取和提交调试信息”里的说明提交 bug 报告。

11.13.2.1. 鼠标问题

某些时候, 唤醒操作会导致鼠标不再正常工作。 已知的绕过这一问题的方法, 是在 /boot/loader.conf 文件中添加 hint.psm.0.flags="0x3000" 设置。 如果这样做不能解决问题, 请考虑按前面介绍的方法提交问题报告。

11.13.2.2. 待机/唤醒

ACPI 有三个挂起到 RAM STR )状态, S1 - S3 和一个挂起到磁盘状态( STD ),称为 S4 STD 可以用两种不同的方式实现。在 S4 BIOS 是由 BIOS -assisted 挂起到磁盘, S4 OS 完全由操作系统来实现。正常状态下的系统是接通电源但没启动操作系统,称为soft off S5 )。

可以使用 sysctl hw.acpi 来查看与休眠有关的项目。 这里是我的 Thinkpad 上得到的结果:

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

这表示我可以使用 acpiconf -s 来测试 S3S4OS, 以及 S5。 如果 s4bios 是一 (1), 则可以使用 S4BIOS 来代替 S4 OS

当测试休眠/唤醒时, 从 S1 开始, 如果它被支持的话。 这个状态是最可能正常工作的状态, 因为它不需要太多的驱动支持。 没有人实现 S2 但如果您有它的支持, 则应该和 S1 类似。 下一件值得尝试的是 S3。 这是最深的 STR 状态, 并需要一系列驱动的支持才能够正常地重新初始化您的硬件。

休眠和唤醒操作最常见的问题是某些设备驱动程序不会保存、 恢复或正确地重新初始化其固件、 寄存器或设备内存。 尝试调试这些问题时, 首先可以尝试:

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

这个测试会模拟休眠和恢复过程而不真的进入 S3 状态。 有时, 您会用这种方式很容易地抓住问题 (例如, 丢失固件状态、 设备 watchdog 超时, 以及一直重试等)。 注意系统不会真的进入 S3 状态, 这意味着这些设备可能不会掉电, 而许多设备在完全不提供休眠和恢复方法时仍可正常工作, 而不像使用真的 S3 状态那样。

较难的情况则需要更多的硬件, 例如用于串口控制台的串口/线, 以及用于 dcons(4) 的火线口/线和内核调试技能。

为了帮助隔离问题, 请在内核中删去尽可能多的驱动。 如果这样做能够解决问题, 请尝试逐个加载驱动直到问题再次出现。 通常预编译的驱动程序如 nvidia.ko、 X11 显示驱动, 以及 USB 的问题最多, 而以太网卡的驱动则通常工作的很好。 如果您能够通过加载和卸载驱动使系统正常工作, 您可以通过将适当的命令放到 /etc/rc.suspend/etc/rc.resume 来将这个过程自动化。 在这两个文件中有一个注释掉的卸载和加载驱动程序的例子供您参考。 另外您还可以将 hw.acpi.reset_video 设置为零 (0), 如果您的显示在唤醒之后显得很混乱。 此外您还可以尝试更长或更短的 hw.acpi.sleep_delay 值看看是否有所助益。

另一件值得一试的事情是使用一个比较新的包含 ACPI 支持的 Linux 发行版来试试看他们的 休眠/唤醒 功能是否在同样的硬件上能够正常工作。 如果在 Linux 下正常, 则很可能是 FreeBSD 驱动程序的问题, 而隔离问题并找到存在问题的驱动有助于解决它。 需要注意的是 ACPI 的维护人员通常并不维护其他驱动 (例如 声音、 ATA, 等等) 因此如果最终发现是驱动的问题最好还是发到 freebsd-current 邮件列表并发给驱动程序的维护者。 如果您喜欢冒险, 则可以加一些 printf(3) 到有问题的驱动中, 以找到它的恢复功能发生问题的位置。

最后, 试试看禁用 ACPI 并代之以启用 APM。 如果 休眠/唤醒 能够在 APM 下正常工作, 使用 APM 可能会更好, 特别是对于较老的硬件 (2000年以前)。 硬件制造商需要一些时间来让老硬件的 ACPI 工作正常, 而 ACPI 的问题十之八九是 BIOS 中的毛病引发的。

11.13.2.3. 系统无响应

绝大多数系统停止响应是由于未能及时响应中断或发生了中断风暴导致的。 芯片组有很多问题最终会溯源到 BIOS 如何在引导系统之前配置中断, APIC (MADT) 表的正确性, 以及 系统控制中断 (SCI) 如何路由。

通过察看 vmstat -i 的输出中包括 acpi0 的那一行可以区分中断风暴和未能及时响应中断。 如果每秒计数器增长的速度多于一两个, 则您是遇到了中断风暴。 如果系统停止了响应, 您可以尝试停止内核并进入 DDB (在控制台上按 CTRL+ALT+ESC) 并输入 show interrupts

处理中断问题的救命稻草是尝试禁用 APIC 支持, 这是通过在 loader.conf 中加入 hint.apic.0.disabled="1" 完成的。

11.13.2.4. 崩溃

崩溃对于 ACPI 是比较罕见的情况, 如果发现, 我们将会非常重视并很快修复它。 您要做的第一件事是设法隔离出能够重现崩溃 (如果可能的话) 的操作并获取一份调用堆栈。 请启用 options DDB 并设置串行控制台 (参见 第 26.6.4 节 “从序列线路(Serial Line)进入DDB除错程序”) 或配置一个 dump(8) 分区。 您将在 DDB 中通过 tr 得到调用堆栈。 如果您只能用手抄的方法记录它, 一定要记下头五 (5) 行和最后五 (5) 行。

然后, 尝试通过在启动时禁用 ACPI 来隔离故障。 如果这样做能够正常工作, 请通过设置 debug.acpi.disable 的那组数值来隔离具体是哪个 ACPI 子系统的问题。 请参见 acpi(4) 联机手册中给出的那些例子。

11.13.2.5. 系统在待机或关机后仍开机

首先请尝试在 loader.conf(5) 中设置 hw.acpi.disable_on_poweroff=0。 这将让 ACPI 不再在关机过程中禁用一些事件。 基于同样的原因, 一些系统需要把这个值设置为 1 (这是默认值)。 这通常能够修复在休眠或关机时立即再次启动的问题。

11.13.2.6. BIOS含有有问题的Bytecode

某些BIOS供应商提供不正确的或错误的字节码。这通常表现为内核控制台消息,如下所示:

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

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

11.13.3. 覆盖预设的AML

BIOS 字节码,又被称为 ACPI 机器语言 ( AML ),由ASL(ACPI Source Language)编译生成。可以在DSDT 表中找到 AML。

FreeBSD 的目标是让所有人能够在不进行任何修改的情况下正常使用ACPI。常见错误(由 BIOS 厂商引起)的解决办法仍在探索中。Microsoft® 的解释器 (acpi.sysacpiec.sys) 没有严格检查是否符合 ACPI 标准,FreeBSD 开发人员将继续寻找并记录 Microsoft® 的解释器允许的 ACPI 非标准行为,并将其移植到 FreeBSD 上,让 FreeBSD 可以在不修复 ASL 的情况下工作。

为了修复错误,您需要提取您系统中的 ASL。可以使用 acpidump-t选项提取 ASL。使用 -d选项将其反编译成 AML。使用以下代码将 ASL 提取到my.asl文件中:

# acpidump -td > my.asl

某些版本的AML会假定用户使用Windows®,为解决此问题,请在/boot/loader.conf中加上hw.acpi.osname="Windows 2009"。将Windows 版本替换为 ASL 中列出的 Windows® 版本。

一些解决方案可能需要修改my.asl。修改完成后需要使用以下命令将其编译城AML。一些警告可以忽略,但编译过程中出现的错误不可忽略,这些错误可能让 ACPI 工作异常。

# iasl -f my.asl

使用-f忽略编译中的错误强制创建AML。一些错误,比如没有返回状态,可以被 FreeBSD 解释器自动修复。

iasl默认输出的文件名是DSDT.aml。若需将修改过的 DSDT 文件加载到内存中,以替换 BIOS 中的有错的 DSDT,请像下面这样修改/boot/loader.conf

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

确认DSDT.aml已被复制到/boot,然后重启系统。如果错误被修复,请使用老ASL和新ASL创建diff(1),并将其发送到freebsd-acpi,这样,FreeBSD 开发者就能在 acpica中修复异常行为。

11.13.4. 获取和提交调试信息

Written by Nate Lawson.
With contributions from Peter SchultzTom Rhodes.

ACPI 驱动程序提供了非常灵活的调试机制。 这允许您指定一组子系统, 以及所需要的详细信息。 需要调试的子系统可以按 layers(层) 来指定, 并分为 ACPI-CA 组件 (ACPI_ALL_COMPONENTS) 和 ACPI 硬件支持 (ACPI_ALL_DRIVERS)。 调试输出的详细程度可以通过 level(详细度) 来指定, 其范围是 ACPI_LV_ERROR (只报告错误) 到 ACPI_LV_VERBOSE (显示所有)。 level 是一个位掩码因此可以一次设置多个选项, 中间用空格分开。 实际使用中您应该考虑使用串行控制台来记录输出, 如果它太长以至于冲掉了控制台消息缓冲的话。 不同的层和输出详细度的完整列表可以在 acpi(4) 联机手册中找到。

调试输出默认并不开启。 要起用它, 您需要在内核设置中添加 options ACPI_DEBUG, 如果您的内核中编入了 ACPI 的话。 您还可以在 /etc/make.conf 中加入 ACPI_DEBUG=1 来在全局起用它。 如果它只是模块, 您可以用下面的方法来重新编译 acpi.ko

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

安装 acpi.ko/boot/kernel and add your 并把所需的详细度和层在 loader.conf 中指定。 这个例子将启用所有 ACPI-CA 组件以及所有 ACPI 硬件驱动 (CPULID, 等等) 的消息。 只输出错误信息, 也就是最低的详细度:

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

如果您需要的信息是由某个特定的事件触发的 (比如说, 休眠之后的唤醒), 您可以不修改 loader.conf 而转而使用 sysctl 来在启动和为那个事件准备系统之后再指定层和详细度。 这些 sysctl 的名字和 loader.conf 中的一致。

一旦收集到调试信息, 就可以将其发送到freebsd-acpi,以便 FreeBSD ACPI 维护者找出问题根源,并给出解决方案。

注意:

在将调试信息提交到此邮件列表之前,请确保安装了最新版本的BIOS,如果可用,则确保安装嵌入式控制器固件版本。

提交问题报告时,请包括以下信息:

  • 错误行为的描述,包括系统类型、模型以及导致出现 Bug 的任何内容。如果 Bug 是新的,则尽可能准确地记下该 Bug 何时开始发生。

  • 运行boot -v后, dmesg的输出,包括此 Bug 产生的所有输出。

  • 如果禁用ACPI有助于解决问题。提交在禁用ACPI的情况下dmesg的输出。

  • 来自sysctl hw.acpi的输出 。这将列出系统提供的功能。

  • ASL 文件的下载地址。请不要直接发送 ASL 文件到邮件列表中,ASL 文件体积太大。使用以下命令生成 ASL 文件:

    # acpidump -dt > name-system.asl

    name替换为登入名并将system替换为制造商/型号,例如njl-FooCo6000.asl

绝大多数 FreeBSD 开发者都会关注FreeBSD-CURRENT 邮件列表,但您还需要将问题发送到 freebsd-acpi 让相关开发者看到问题。如果 bug 隐藏比较深,您还需要提交相关错误报告。发送 PR 时请包含上述所有信息,这有助于让开发者更快的了解错误情况。在发送 PR 前请先发送邮件到freebsd-acpi,看看以前是否有相同情况。

11.13.5. 参考文献

有关ACPI的更多信息,请访问:

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.