调试休眠和挂起

  1. 2007 Rafael J. Wysocki <rjw@sisk.pl>, GPL

1. 测试休眠(又名挂起到磁盘或 STD)

要检查休眠是否工作,您可以尝试在“重启”模式下休眠

# echo reboot > /sys/power/disk
# echo disk > /sys/power/state

系统应该创建一个休眠镜像,重启,恢复并回到您开始转换的命令提示符。如果发生这种情况,休眠很可能工作正常。不过,您需要连续重复测试至少几次以增加信心。[这是必要的,因为一些问题只在第二次尝试挂起和恢复系统时才会出现。] 此外,在“重启”和“关机”模式下休眠会导致 PM 核心跳过一些平台相关的回调,这在 ACPI 系统上可能是使休眠工作的必要条件。因此,如果您的机器在“重启”模式下休眠或恢复失败,您应该尝试“平台”模式

# echo platform > /sys/power/disk
# echo disk > /sys/power/state

这是休眠的默认和推荐模式。

不幸的是,休眠的“平台”模式在一些 BIOS 损坏的系统上不起作用。在这种情况下,休眠的“关机”模式可能有效

# echo shutdown > /sys/power/disk
# echo disk > /sys/power/state

(它类似于“重启”模式,但需要您按下电源按钮才能使系统恢复)。

如果“平台”和“关机”休眠模式都不起作用,您将需要确定哪里出了问题。

a) 测试休眠模式

要找出为什么您的系统休眠失败,您可以使用在编译内核时设置了 CONFIG_PM_DEBUG 的特殊测试工具。然后,有一个文件 /sys/power/pm_test 可用于使休眠核心在测试模式下运行。有 5 种测试模式可用

freezer
  • 测试进程的冻结

devices
  • 测试进程的冻结和设备的挂起

platform
  • 测试进程的冻结,设备的挂起和平台全局控制方法 [1]

processors
  • 测试进程的冻结,设备的挂起,平台全局控制方法 [1] 和禁用非引导 CPU

core
  • 测试进程的冻结,设备的挂起,平台全局控制方法[1],禁用非引导 CPU 和挂起平台/系统设备

要使用其中一种,必须将相应的字符串写入 /sys/power/pm_test(例如,“devices”测试进程的冻结和挂起设备),并发出标准的休眠命令。例如,要将“devices”测试模式与休眠的“平台”模式一起使用,您应该执行以下操作

# echo devices > /sys/power/pm_test
# echo platform > /sys/power/disk
# echo disk > /sys/power/state

然后,内核将尝试冻结进程,挂起设备,等待几秒钟(默认 5 秒,但可以通过 suspend.pm_test_delay 模块参数配置),恢复设备并解冻进程。如果将“platform”写入 /sys/power/pm_test ,则在挂起设备后,内核还将调用用于为休眠准备平台固件的全局控制方法(例如,ACPI 全局控制方法)。接下来,它将等待可配置的秒数,并调用用于取消休眠等的平台(例如,ACPI)全局方法。

将“none”写入 /sys/power/pm_test 会导致内核切换到正常的休眠/挂起操作。此外,当打开读取时,/sys/power/pm_test 包含所有可用测试的空格分隔列表(包括表示正常功能的“none”),其中当前测试级别用方括号表示。

通常,如您所见,每个测试级别都比前一个测试级别更“具有侵入性”,并且“core”级别在不创建休眠镜像的情况下尽可能深入地测试硬件和驱动程序。显然,如果“devices”测试失败,“platform”测试也会失败,依此类推。因此,作为经验法则,您应该尝试从“freezer”开始的测试模式,通过“devices”,“platform”和“processors”直到“core”(在每个级别重复测试几次,以确保避免任何随机因素)。

如果“freezer”测试失败,则表示存在无法冻结的任务(在这种情况下,通常可以通过分析失败测试后获得的 dmesg 输出,来识别有问题的任务)。此级别的失败通常意味着任务冻结子系统存在问题,应予以报告。

如果“devices”测试失败,则很可能存在无法挂起或恢复其设备的驱动程序(在后一种情况下,系统可能会在测试后挂起或变得不稳定,因此请考虑这一点)。要查找此驱动程序,您可以按照规则进行二进制搜索

  • 如果测试失败,则卸载当前加载的一半驱动程序并重复(这可能涉及重新启动系统,因此请始终注意测试前已加载的驱动程序),

  • 如果测试成功,则加载您最近卸载的一半驱动程序并重复。

一旦您找到失败的驱动程序(可能不止一个),您必须在每次休眠之前卸载它。在这种情况下,请务必报告驱动程序的问题。

您也可以在卸载所有模块后,“devices”测试仍然失败。在这种情况下,您可能需要在内核配置中查找可以编译为模块的驱动程序(并使用编译为模块的这些驱动程序再次测试)。您还可以尝试使用一些特殊的内核命令行选项,例如“noapic”,“noacpi”甚至“acpi=off”。

如果“platform”测试失败,则表示您的系统上平台(例如,ACPI)固件的处理存在问题。在这种情况下,休眠的“平台”模式不太可能工作。您可以尝试“关机”模式,但这只是一种权宜之计。

如果“processors”测试失败,则表示禁用/启用非引导 CPU 不起作用(当然,这可能只是 SMP 系统上的问题),应报告该问题。在这种情况下,您还可以尝试使用 /sys/devices/system/cpu/cpu*/online sysfs 属性关闭和打开非引导 CPU,看看是否有效。

如果“core”测试失败,这意味着挂起系统/平台设备失败(这些设备在关闭中断的一个 CPU 上被挂起),则问题很可能与硬件相关且很严重,因此应该报告。

任何“平台”,“processors”或“core”测试的失败都可能导致您的系统挂起或变得不稳定,因此请注意。这种失败通常表明一个严重的问题,很可能与硬件有关,但请务必报告。

b) 测试最小配置

如果所有休眠测试模式都有效,则可以使用“init=/bin/bash”命令行参数启动系统,并尝试在“重启”,“关机”和“平台”模式下休眠。如果这不起作用,则可能存在静态编译到内核中的驱动程序的问题,您可以尝试将更多驱动程序编译为模块,以便可以单独测试它们。否则,模块化驱动程序存在问题,您可以按照以下算法加载一半您通常使用的模块并进行二进制搜索:- 如果加载了 n 个模块且尝试挂起和恢复失败,则卸载 n/2 个模块并重试(这可能涉及重新启动系统),- 如果加载了 n 个模块且尝试挂起和恢复成功,则加载另外 n/2 个模块并重试。

同样,如果您找到有问题的模块,则必须在每次休眠之前卸载它(它们),并且请报告其问题。

c) 使用“test_resume”休眠选项

/sys/power/disk 通常会告诉内核在创建休眠镜像后该怎么做。其中一个可用选项是“test_resume”,它会导致刚创建的镜像用于立即恢复。即,执行

# echo test_resume > /sys/power/disk
# echo disk > /sys/power/state

将创建一个休眠镜像,并立即从中触发恢复,而无需以任何方式涉及平台固件。

该测试可用于检查从休眠恢复失败是否与平台固件的错误交互有关。也就是说,如果以上方法每次都有效,但从实际休眠恢复不起作用或不可靠,则平台固件可能对失败负责。

在支持使用不同内核来恢复休眠镜像的架构和平台上(也就是说,用于从存储读取镜像并将其加载到内存中的内核与镜像中包含的内核不同),或者支持内核地址空间随机化的架构和平台上,它也可以用来检查恢复失败是否与恢复内核和镜像内核之间的差异有关。

d) 高级调试

如果休眠在你的系统上即使在最小配置下也无法工作,并且将更多驱动程序编译为模块不切实际,或者某些模块无法卸载,你可以使用更高级的调试技术来查找问题。首先,如果你的机器上有串口,你可以使用 'no_console_suspend' 参数启动内核,并尝试使用串行控制台记录内核消息。这可能会提供一些关于挂起(恢复)失败原因的信息。或者,可以使用 FireWire 端口通过 firescope 进行调试 (http://v3.sk/~lkundrak/firescope/)。在 x86 上,也可以使用 如何让 s2ram 工作 中记录的 PM_TRACE 机制。

2. 测试挂起到内存 (STR)

要验证 STR 是否工作,通常更方便的方法是使用 http://suspend.sf.net 上提供的 s2ram 工具,该工具的文档位于 http://en.opensuse.org/SDB:Suspend_to_RAM (S2RAM_LINK)。

具体而言,在将“freezer”、“devices”、“platform”、“processors”或“core”写入 /sys/power/pm_test(如果内核使用 CONFIG_PM_DEBUG 设置编译则可用)后,挂起代码将以与给定字符串对应的测试模式工作。STR 测试模式的定义方式与休眠相同,因此请参阅第 1 节以获取有关它们的更多信息。特别是,“core”测试允许你测试除了实际调用平台固件以使系统进入睡眠状态之外的所有内容。

除此之外,借助 /sys/power/pm_test 进行测试可以帮助你识别未能挂起或恢复其设备的驱动程序。每次进行 STR 转换之前,都应卸载它们。

接下来,你可以按照 S2RAM_LINK 的说明测试系统,但如果它不能“开箱即用”,你可能需要使用“init=/bin/bash”启动它,并在最小配置下测试 s2ram。在这种情况下,你可能可以通过遵循与第 1 节中描述的过程类似的过程来搜索失败的驱动程序。如果发现一些失败的驱动程序,你将必须在每次 STR 转换之前(即在运行 s2ram 之前)卸载它们,并请报告它们的问题。

有一个 debugfs 条目显示了挂起到内存的统计信息。以下是其输出示例

# mount -t debugfs none /sys/kernel/debug
# cat /sys/kernel/debug/suspend_stats
success: 20
fail: 5
failed_freeze: 0
failed_prepare: 0
failed_suspend: 5
failed_suspend_noirq: 0
failed_resume: 0
failed_resume_noirq: 0
failures:
  last_failed_dev:      alarm
                        adc
  last_failed_errno:    -16
                        -16
  last_failed_step:     suspend
                        suspend

字段 success 表示挂起到内存的成功次数,字段 fail 表示失败次数。其他的是挂起到内存的不同步骤的失败次数。suspend_stats 仅列出最后 2 个失败的设备、错误号和挂起的失败步骤。