调试休眠和挂起功能
2007年 Rafael J. Wysocki rjw@sisk.pl,GPL
1. 测试休眠(也称为挂起到磁盘或STD)
要检查休眠是否正常工作,您可以尝试在“重启”模式下进行休眠:
# echo reboot > /sys/power/disk # echo disk > /sys/power/state
系统应该会创建一个休眠镜像,重新启动,恢复并回到您启动转换的命令提示符。如果发生这种情况,休眠很可能正常工作。不过,您需要至少连续重复测试几次以确保可靠性。[这是必要的,因为有些问题只会在第二次尝试挂起和恢复系统时出现。] 此外,在“重启”和“关机”模式下进行休眠会导致电源管理核心跳过一些可能对ACPI系统进行休眠所必需的平台相关回调。因此,如果您的计算机在“重启”模式下无法休眠或恢复,您应该尝试“platform”模式:
# echo platform > /sys/power/disk # echo disk > /sys/power/state
这是休眠的默认和推荐模式。
不幸的是,一些具有破损BIOS的系统无法使用“platform”模式进行休眠。在这种情况下,“关机”模式的休眠可能会起作用:
# echo shutdown > /sys/power/disk # echo disk > /sys/power/state
(它类似于“重启”模式,但需要您按电源按钮来使系统恢复)。
如果既不是“platform”模式也不是“shutdown”模式的休眠工作,您需要确定出现了什么问题。
a) 测试休眠模式
要找出为什么休眠在您的系统上失败,您可以使用一个特殊的测试设施,如果内核编译时启用了CONFIG_PM_DEBUG选项,则可以使用该设施。然后,有一个文件/sys/power/pm_test,可以用来使休眠核心以测试模式运行。有5种测试模式可用:
- freezer:测试进程的冻结
- devices:测试进程的冻结和设备的挂起
- platform:测试进程的冻结、设备的挂起和平台全局控制方法
- processors:测试进程的冻结、设备的挂起、平台全局控制方法和禁用非引导CPU
- core:测试进程的冻结、设备的挂起、平台全局控制方法、禁用非引导CPU和挂起平台/系统设备
要使用其中之一,需要将相应的字符串写入/sys/power/pm_test(例如,将“devices”写入以测试进程的冻结和设备的挂起),然后发出标准的休眠命令。例如,要在“platform”休眠模式下使用“devices”测试模式,您应该执行以下操作:
# echo devices > /sys/power/pm_test # echo platform > /sys/power/disk # echo disk > /sys/power/state
然后,内核将尝试冻结进程、挂起设备,等待几秒钟(默认为5秒,但可以通过suspend.pm_test_delay模块参数进行配置),恢复设备并解冻进程。如果在/sys/power/pm_test中写入“platform”,那么在挂起设备后,内核将另外调用全局控制方法(例如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)固件的问题。在这种情况下,“platform”休眠模式不太可能起作用。您可以尝试“shutdown”模式,但那只是一个不太理想的权宜之计。
如果“processors”测试失败,表示禁用/启用非引导CPU不起作用(当然,这可能只是多处理器系统上的问题),应该进行报告。在这种情况下,您还可以尝试使用/sys/devices/system/cpu/cpu*/online sysfs属性关闭和打开非引导CPU,看看是否有效。
如果“core”测试失败,表示系统/平台设备的挂起失败(这些设备在关闭中断的一个CPU上被挂起),问题很可能与硬件有关且严重,应该进行报告。
任何“platform”、“processors”或“core”测试的失败都可能导致系统挂起或变得不稳定,因此请注意。这种失败通常表示可能与硬件有关的严重问题,但无论如何,请进行报告。
b) 测试最小配置
如果所有的休眠测试模式都正常工作,您可以使用“init=/bin/bash”命令行参数引导系统,并尝试在“重启”、“关机”和“platform”模式下进行休眠。如果这样做不起作用,可能是由于某个驱动程序静态编译到内核中的问题,您可以尝试将更多的驱动程序编译为模块,以便可以单独测试它们。否则,可能是由于模块化驱动程序的问题,您可以加载一半您通常使用的模块,并根据以下算法进行二分搜索:
- 如果有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上,还可以使用PM_TRACE机制,该机制在《如何使s2ram工作》中有文档记录。
2. 测试挂起到RAM(STR)
要验证STR是否正常工作,通常更方便的方法是使用http://suspend.sf.net上提供的s2ram工具,并在http://en.opensuse.org/SDB:Suspend_to_RAM(S2RAM_LINK)中有文档记录。
也就是说,在/sys/power/pm_test中写入“freezer”、“devices”、“platform”、“processors”或“core”(如果内核编译时启用了CONFIG_PM_DEBUG选项)后,挂起代码将以与给定字符串对应的测试模式运行。STR测试模式的定义方式与休眠的方式相同,因此有关它们的更多信息,请参阅第1节。特别是,“core”测试允许您测试除了实际调用平台固件以将系统置于睡眠状态之外的所有内容。
通过使用/sys/power/pm_test进行测试,您可以识别无法挂起或恢复其设备的驱动程序。它们在每次STR转换之前都必须被卸载。
接下来,您可以按照S2RAM_LINK中的说明来测试系统,但如果它不能“开箱即用”,您可能需要使用“init=/bin/bash”引导它,并在最小配置下测试s2ram。在这种情况下,您可以按照与第1节中描述的类似的程序搜索失败的驱动程序。如果找到了一些失败的驱动程序,您必须在每次STR转换之前卸载它们(即在运行s2ram之前),并请报告有关它们的问题。
有一个debugfs条目显示了挂起到RAM的统计信息。以下是它的输出示例:
# 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表示成功挂起到RAM的次数,字段fail表示失败次数。其他字段是挂起到RAM不同步骤的失败次数。suspend_stats列出了最后两个失败设备、错误号和挂起步骤。