Linux内核suspend状态
Linux内核支持多种类型的睡眠状态,通过设置不同的模块进入低功耗模式来达到省电功能。目前存在四种模式:suspend to idle、power-on standby(Standby)、suspend to ram(STR)和sudpend to disk(Hibernate),分别对应ACPI状态的S0、S1、S3和S4。
Suspend to idle完全是软件相关的并且尽量将CPU维持在深度idle状态。
Power-on standby设置设备进入低功耗模式并且关闭所有non-boot CPU。
Suspend to ram就更进一步,关闭所有CPU并且设置RAM进入自刷新模式。(在HiKey的实际测试中,boot CPU是没有关闭的!实际上这里也没有standby,mem和standby基本上没有区别。)
Suspend to disk是最省功耗的模式,通过尽可能的关闭设备,包括RAM。RAM的数据会被写入磁盘中,在resume的时候读回到RAM。
下面用STR表示Suspend to RAM,STI表示Suspend to Idle。
详情请参考:http://www.linaro.org/blog/suspend-to-idle/
STR 和STI区别
写入/sys/power/state不同字符串,可以让系统进入不同睡眠状态。
#define PM_SUSPEND_ON ((__force suspend_state_t) 0) 正常工作状态 #define PM_SUSPEND_FREEZE ((__force suspend_state_t) 1) 对应suspend to idle #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2) 对应power-on standby #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) 对应suspend to idle #define PM_SUSPEND_MIN PM_SUSPEND_FREEZE #define PM_SUSPEND_MAX ((__force suspend_state_t) 4) 对应suspend to disk,即hibernate
|
针对state sysfs节点的写入,最终会进入到state_store这个函数,将字符串转换成上表中不同状态。
state_store(kernel/power/main.c) -->pm_suspend (kernel/power/suspend.c) 处理除freeze、standby、mem三种类型suspend -->enter_state 在进入睡眠之前,做一些准备工作 -->suspend_devices_and_enter -->suspend_enter 这里才是freeze与standby/mem区别所在。 -->hibernate 进入suspend to disk流程 |
STR和STI的最主要区别就是下面一段代码:
static int suspend_enter(suspend_state_t state, bool *wakeup) {
…
/* * PM_SUSPEND_FREEZE equals * frozen processes + suspended devices + idle processors. * Thus we should invoke freeze_enter() soon after * all the devices are suspended. */ if (state == PM_SUSPEND_FREEZE) { 如果要进入freeze状态,就会执行此段代码。 trace_suspend_resume(TPS("machine_suspend"), state, true); freeze_enter(); trace_suspend_resume(TPS("machine_suspend"), state, false); goto Platform_wake; 在执行结束跳转到Platform_wake,中间一段绿色代码将会被跳过。所以说freeze和standby、mem相比,多了freeze_enter,少了对non-boot CPUs、arch、syscore的操作。 }
error = disable_nonboot_cpus(); if (error || suspend_test(TEST_CPUS)) { log_suspend_abort_reason("Disabling non-boot cpus failed"); goto Enable_cpus; }
arch_suspend_disable_irqs(); BUG_ON(!irqs_disabled());
error = syscore_suspend(); if (!error) { *wakeup = pm_wakeup_pending(); if (!(suspend_test(TEST_CORE) || *wakeup)) { trace_suspend_resume(TPS("machine_suspend"), state, true); error = suspend_ops->enter(state); trace_suspend_resume(TPS("machine_suspend"), state, false); events_check_enabled = false; } else if (*wakeup) { pm_get_active_wakeup_sources(suspend_abort, MAX_SUSPEND_ABORT_LEN); log_suspend_abort_reason(suspend_abort); error = -EBUSY; } syscore_resume(); }
arch_suspend_enable_irqs(); BUG_ON(irqs_disabled());
Enable_cpus: enable_nonboot_cpus();
Platform_wake: platform_resume_noirq(state); dpm_resume_noirq(PMSG_RESUME);
…
}
|
下面分析一些每个子系统的suspend/resume。
整个suspend可以分为若干阶段,每个阶段函数—>关键节点Trace—>analyze_suspend.py解析Trace—>根据Trace时间画出Timeline图表
这样就可以分析出总的时间差异,每个阶段差异,甚至一个设备suspend/resume、一个子系统suspend/resume的时间差异。
freeze_enter
platform_suspend_begin/patform_resume_end
suspend_console/resume_console
dpm_suspend_start/dpm_resume_end
dpm_suspend_noirq/dpm_resume_noirq
disable_nonboot_cpus/enable_nonboot_cpus
arch_suspend_disable_irqs/arch_suspend_enable_irqs
syscore_suspend/syscore_resume
如何让HiKey进入STR/STI并唤醒?
可以通过配置GPIO作为唤醒源,或者通过RTC作为唤醒源,延时一定时间来唤醒。
检查是否存在/sys/class/rtc/rtc0/wakealarm,入不存在则需要打开CONFIG_RTC_DRV_PL031。
写入wakealarm的参数,表示在多少秒之后resume唤醒,退出suspend。
写mem进入state,是系统进入suspend流程。
adb root && adb remount adb shell "echo +10 > /sys/class/rtc/rtc0/wakealarm && echo mem > /sys/power/state" |
suspend/resume的latency分析手段
analyze_suspend.py v3.0
在kernel的scripts中,这个工具可以帮助内核和OS开发者优化suspend/resume时间。
在打开一系列内核选项之后,此工具就可以执行suspend操作,然后抓取dmesg和ftrace数据知道resume结束。
这些数据会按照时间线显示每个设备,并且显示占用最多suspend/resume时间的设备或者子系统的调用关系详图。
执行工具后,会根据时间生成一个子目录,里面包含:html、dmesg和原始ftrace文件。
下面简单看一下工具选项:
Options: [general] -h Print this help text -v Print the current tool version -verbose Print extra information during execution and analysis -status Test to see if the system is enabled to run this tool -modes List available suspend modes 显示当前支持的suspend模式 -m mode Mode to initiate for suspend ['freeze', 'mem', 'disk'] (default: mem) 设置进入何种模式的suspend -rtcwake t Use rtcwake to autoresume after <t> seconds (default: disabled) 使用rtc来唤醒,参数是间隔时间 [advanced] -f Use ftrace to create device callgraphs (default: disabled) 基于ftrace生成调用关系图 -filter "d1 d2 ..." Filter out all but this list of dev names -x2 Run two suspend/resumes back to back (default: disabled) -x2delay t Minimum millisecond delay <t> between the two test runs (default: 0 ms) -postres t Time after resume completion to wait for post-resume events (default: 0 S) -multi n d Execute <n> consecutive tests at <d> seconds intervals. The outputs will be created in a new subdirectory with a summary page. [utilities] -fpdt Print out the contents of the ACPI Firmware Performance Data Table -usbtopo Print out the current USB topology with power info -usbauto Enable autosuspend for all connected USB devices [android testing] -adb binary Use the given adb binary to run the test on an android device. 参数需要给出adb路径,工具就会对Android设备进行测试,并将结果pull出来。有一点需要注意,在此之前确保adb具有root权限。 The device should already be connected and with root access. Commands will be executed on the device using "adb shell" [re-analyze data from previous runs] 针对之前测试数据重新分析 -ftrace ftracefile Create HTML output using ftrace input -dmesg dmesgfile Create HTML output using dmesg (not needed for kernel >= 3.15) -summary directory Create a summary of all test in this dir
|
在了解了工具使用方法之后,就可以进行相关测试了。
Android
./analysze_suspend.py –modes –adb /usr/bin/adb获取当前系统支持的suspend状态。
1.Android上测试STR,suspend/resume共5次,每次间隔20秒。
./analyze_suspend.py -adb /usr/bin/adb -rtcwake 10 -multi 5 20 -f -m mem |
2.Android上测试STI,suspend/resume共10次,每次间隔5秒。
./analyze_suspend.py -adb /usr/bin/adb -rtcwake 10 -multi 5 20 -f -m freeze |
测试结果可以在如下获得:
https://github.com/arnoldlu/common-use/tree/master/tools/analyze_suspend/hikey_test
存在的问题:analyze_suspend.py不支持Android的rtcwakeup和callgraph。已经在如下fix:
https://github.com/arnoldlu/common-use/blob/master/tools/analyze_suspend/analyze_suspend.py
总体对比
下面是HiKey上测试结果,可以看出两个数据都不够稳定。mem的suspend和resume平均值都比较高。
freeze相比mem的suspend/resume平均值提高了304.3ms/613.5ms。
是否suspend CPU
对比如下两幅图,明显看出mem类型的suspend关闭了除CPU0之外的所有CPU;而freeze则没有关闭任何CPU。
non-boot CPUs的suspend/resume时间就达到300ms/200ms。
同时从log中也可以看出mem和freeze的主要区别就在于是否disabling/enabling non-boot CPU。其他设备和子系统的suspend/resume时间基本一致。
[ 3385.642962] PM: suspend entry 1970-01-01 00:57:30.580909763 UTC [ 3385.649165] PM: Syncing filesystems ... done. [ 3385.661349] Freezing user space processes ... [ 3385.671207] dwc2 f72c0000.usb: dwc2_hsotg_ep_stop_xfr: timeout DOEPCTL.EPDisable [ 3385.678933] dwc2 f72c0000.usb: GINNakEff triggered [ 3385.685718] (elapsed 0.019 seconds) done. [ 3385.689860] Freezing remaining freezable tasks ... (elapsed 0.002 seconds) done. [ 3385.700092] Suspending console(s) (use no_console_suspend to debug) [ 3385.736020] PM: suspend of devices complete after 27.195 msecs [ 3385.740811] PM: late suspend of devices complete after 4.765 msecs [ 3385.743919] PM: noirq suspend of devices complete after 3.090 msecs Disabling and Enabling non-boot CPUs [ 3386.209126] PM: noirq resume of devices complete after 1.865 msecs [ 3386.212066] PM: early resume of devices complete after 2.460 msecs [ 3386.234729] mmc_host mmc0: Bus speed (slot 0) = 24800000Hz (slot req 400000Hz, actual 400000HZ div = 31) [ 3386.311480] mmc_host mmc0: Bus speed (slot 0) = 51756522Hz (slot req 52000000Hz, actual 51756522HZ div = 0) [ 3386.410411] mmc_host mmc2: Bus speed (slot 0) = 24800000Hz (slot req 400000Hz, actual 400000HZ div = 31) [ 3386.458232] mmc_host mmc2: Bus speed (slot 0) = 24800000Hz (slot req 25000000Hz, actual 24800000HZ div = 0) [ 3386.458729] PM: resume of devices complete after 246.646 msecs [ 3386.818770] Restarting tasks ... [ 3386.827026] done. [ 3386.844139] PM: suspend exit 1970-01-01 00:57:40.624589167 UTC
|
[ 3471.760265] PM: Syncing filesystems ... done. [ 3471.771897] Freezing user space processes ... [ 3471.780407] dwc2 f72c0000.usb: dwc2_hsotg_ep_stop_xfr: timeout DOEPCTL.EPDisable [ 3471.788105] dwc2 f72c0000.usb: GINNakEff triggered [ 3471.794916] (elapsed 0.018 seconds) done. [ 3471.799078] Freezing remaining freezable tasks ... (elapsed 0.002 seconds) done. [ 3471.809320] Suspending console(s) (use no_console_suspend to debug) [ 3471.847947] PM: suspend of devices complete after 29.905 msecs [ 3471.852473] PM: late suspend of devices complete after 4.497 msecs [ 3471.855611] PM: noirq suspend of devices complete after 3.120 msecs
[ 3481.034722] PM: noirq resume of devices complete after 1.945 msecs [ 3481.037992] PM: early resume of devices complete after 2.694 msecs [ 3481.062803] mmc_host mmc0: Bus speed (slot 0) = 24800000Hz (slot req 400000Hz, actual 400000HZ div = 31) [ 3481.137795] mmc_host mmc0: Bus speed (slot 0) = 51756522Hz (slot req 52000000Hz, actual 51756522HZ div = 0) [ 3481.234796] mmc_host mmc2: Bus speed (slot 0) = 24800000Hz (slot req 400000Hz, actual 400000HZ div = 31) [ 3481.278601] mmc_host mmc2: Bus speed (slot 0) = 24800000Hz (slot req 25000000Hz, actual 24800000HZ div = 0) [ 3481.279396] PM: resume of devices complete after 241.388 msecs [ 3481.358513] Restarting tasks ... done. [ 3481.377766] PM: suspend exit 1970-01-01 00:59:15.332218333 UTC
|
resume_console节省时间
对比resume_console可以发现,mem要比freeze多210ms。
Ubuntu
此工具在Ubuntu上显示了更强大的功能。
支持了callgraph功能之后,更能清晰地分析每个设备或者子系统的suspend/resume占用的时间。
sudo ./analyze_suspend.py -rtcwake 10 -multi 5 20 -f -m mem sudo ./analyze_suspend.py -rtcwake 10 -multi 5 20 -f -m freeze |
在对比两种不同suspend模式后,发现freeze花费的时间要比mem少。这也符合预期,但是没有功耗数据?_?。
下面着重分析一下如何基于此工具分析。
工具界面总体分析
最上面显示Kernel Suspend Time和Kernel Resume Time,可以从总体上查看是否有回退或者进步。
再下面是一些缩放按钮。
然后就是基于timeline的图表,比对颜色示意图,可以清晰看出suspend prepare、suspend、suspend late、suspend irq、suspend machine、resume machine、resume irq、resume early、resume和resume complete的分布。
最下面是每个模块、子系统的详细函数调用图以及开始时间、消耗时间。
子系统、模块详细分析
选中一个模块,会在最下面显示详细的模块在suspend/resume各个阶段消费的时间,以及函数调用关系图。
缩放查看细节
ZOOM IN放大,ZOOMOUT缩小,ZOOM 1:1恢复原始尺寸。
通过在timeline图表,放大可以查看到更小的模块消耗的时间。从宏观到模块,再到函数消耗时间,逐步细化,很有利于分析。
如果发现某个函数占用时间较大,可以逐级展开。知道发现最终占用较大的函数,发现问题所在。
参考文档
Power Management Support in Hikey (suspend-resume):http://www.96boards.org/forums/topic/power-management-support-in-hikey-suspend-resume/#gsc.tab=0
Suspend to Idle:http://www.linaro.org/blog/suspend-to-idle/
Suspend and Resume:https://01.org/zh/suspendresume
SuspendAndResume github:https://github.com/arnoldlu/suspendresume
Linux电源管理(6)_Generic PM之Suspend功能:http://www.wowotech.net/pm_subsystem/suspend_and_resume.html