更多linux系统电源管理相关的内容请看:https://blog.csdn.net/u010936265/article/details/146436725
1 简介
在Linux操作系统中,系统睡眠(Sleep)也常称为挂起(Suspend,但在某些场合Suspend特指Suspend To RAM),在ACPI标准的定义中,系统睡眠状态总称Sx,包括不同的级别,分别为S0~S5。S0实际上是运行时的活动状态,而S5实际上是软关机状态,剩下的S1~S4则是不同程度的睡眠状态。各种睡眠状态遵循一个总的原则:睡眠状态越深,功耗越低,唤醒时间也越长。
《用“芯”探核:基于龙芯的Linux内核探索解析》8.3 系统级睡眠管理
《Linux设备驱动开发详解:基于最新的Linux4.0内核》19.9 挂起到RAM;图19.9
本文主要基于linux-5.4.18版本的内核代码进行分析。
2 s2idle
2.1 触发s2idle
方法一
echo freeze > /sys/power/state
方法二
echo s2idle > /sys/power/mem_sleep
echo mem > /sys/power/state
2.2 代码流程
2.2.1 代码大致流程
pm_suspend();
-> enter_state();
-> trace_suspend_resume(TPS("suspend_enter"), state, true);
-> suspend_prepare();
-> pm_pr_dbg("Suspending system (%s)\n", ...);
-> suspend_devices_and_enter();
-> suspend_enter();
-> s2idle_loop();
-> s2idle_enter();
-> trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, true);
-> swait_event_exclusive(s2idle_wait_head, s2idle_state == S2IDLE_STATE_WAKE); //进入idle,等待被唤醒
-> trace_suspend_resume(TPS("machine_suspend"), PM_SUSPEND_TO_IDLE, false);
-> pm_pr_dbg("resume from suspend-to-idle\n");
-> pm_pr_dbg("Finishing wakeup.\n");
-> suspend_finish();
-> suspend_thaw_processes();
-> pm_notifier_call_chain(PM_POST_SUSPEND);
2.2.2 进入idle后被唤醒的大致流程
2.2.2 详细流程分析 (ARM64)
Linux电源管理——Suspend-to-Idle(s2idle) 流程-CSDN博客
2.3 查看程序流程信息
# echo 1 > /sys/kernel/debug/tracing/events/power/suspend_resume/enable
# cat /sys/kernel/debug/tracing/trace_pipe (然后在另外一个终端运行:echo freeze > /sys/power/state)
bash-8014 [000] ..... 262714.426077: suspend_resume: suspend_enter[1] begin
bash-8014 [000] ..... 262714.426083: suspend_resume: sync_filesystems[0] begin
bash-8014 [000] ..... 262714.442611: suspend_resume: sync_filesystems[0] end
bash-8014 [000] ..... 262714.564293: suspend_resume: freeze_processes[0] begin
bash-8014 [000] ..... 262714.568173: suspend_resume: freeze_processes[0] end
bash-8014 [000] ..... 262714.568176: suspend_resume: suspend_enter[1] end
bash-8014 [000] ..... 262714.568178: suspend_resume: dpm_prepare[2] begin
bash-8014 [000] ..... 262714.568950: suspend_resume: dpm_prepare[2] end
bash-8014 [000] ..... 262714.568950: suspend_resume: dpm_suspend[2] begin
bash-8014 [000] ..... 262715.679473: suspend_resume: dpm_suspend[2] end
bash-8014 [000] ..... 262715.679475: suspend_resume: dpm_suspend_late[2] begin
bash-8014 [000] ..... 262715.701106: suspend_resume: dpm_suspend_late[2] end
bash-8014 [000] ..... 262715.706723: suspend_resume: dpm_suspend_noirq[2] begin
bash-8014 [000] ..... 262715.772909: suspend_resume: dpm_suspend_noirq[2] end
bash-8014 [000] ..... 262715.778395: suspend_resume: machine_suspend[1] begin
<idle>-0 [006] d.... 262715.778423: suspend_resume: timekeeping_freeze[6] begin
<idle>-0 [001] dN... 262759.402157: suspend_resume: timekeeping_freeze[1] end //开始唤醒机器
bash-8014 [004] ..... 262759.402305: suspend_resume: machine_suspend[1] end
bash-8014 [004] ..... 262759.428208: suspend_resume: dpm_resume_noirq[16] begin
bash-8014 [003] ..... 262759.948089: suspend_resume: dpm_resume_noirq[16] end
bash-8014 [003] ..... 262759.952409: suspend_resume: dpm_resume_early[16] begin
bash-8014 [003] ..... 262759.954209: suspend_resume: dpm_resume_early[16] end
bash-8014 [003] ..... 262759.954210: suspend_resume: dpm_resume[16] begin
bash-8014 [007] ..... 262761.781238: suspend_resume: dpm_resume[16] end
bash-8014 [007] ..... 262761.781239: suspend_resume: dpm_complete[16] begin
bash-8014 [007] ..... 262761.789535: suspend_resume: dpm_complete[16] end
bash-8014 [007] ..... 262761.789536: suspend_resume: resume_console[1] begin
bash-8014 [007] ..... 262761.789539: suspend_resume: resume_console[1] end
bash-8014 [007] ..... 262761.789540: suspend_resume: thaw_processes[0] begin
bash-8014 [007] ..... 262761.791865: suspend_resume: thaw_processes[0] end
3 挂起到RAM(STR) 和 唤醒
3.1 简介
CPU和外设全部断电,内存供电但处于低耗能的⾃刷新状态(内容保持不变)。 典型功耗为开机状态的1%左右,唤醒时间5〜10秒。必须通过电源按键唤醒。
《⽤“芯”探核:基于⻰芯的Linux内核探索解析》 8.1 电源管理概述
3.2 示意图
《Linux设备驱动开发详解:基于最新的Linux4.0内核》19.9 挂起到RAM;图19.10
3.3 触发挂起到RAM
第一步、设置mem_sleep参数
方法一、通过启动参数设置,在启动参数里添加下面内容:
mem_sleep_default=deep
方法二、通过/sys/power/mem_sleep设置:
echo deep > /sys/power/mem_sleep
第二步、 触发挂起到RAM操作
echo mem > /sys/power/state
3.4 代码流程
3.4.1 代码大致流程
pm_suspend();
-> enter_state();
-> trace_suspend_resume(TPS("suspend_enter"), state, true);
-> suspend_prepare();
-> __pm_notifier_call_chain(PM_SUSPEND_PREPARE, ...);
-> trace_suspend_resume(TPS("freeze_processes"), 0, true);
-> suspend_freeze_processes();
-> trace_suspend_resume(TPS("freeze_processes"), 0, false);
-> pm_pr_dbg("Suspending system (%s)\n", ...);
-> suspend_devices_and_enter();
-> suspend_enter();
-> platform_suspend_prepare();
-> platform_suspend_prepare_late();
-> dpm_suspend_noirq();
-> platform_suspend_prepare_noirq();
-> trace_suspend_resume(TPS("machine_suspend"), state, true);
-> suspend_ops->enter(state); //进入睡眠,等待被唤醒,被唤醒后,继续执行后面的代码
-> trace_suspend_resume(TPS("machine_suspend"), state, false);
-> pm_pr_dbg("Finishing wakeup.\n");
-> suspend_finish();
-> suspend_thaw_processes();
-> pm_notifier_call_chain(PM_POST_SUSPEND);
3.4.2 详细流程
Linux电源管理——系统Suspend/Resume流程_skip esd during panel suspend-CSDN博客
Linux电源管理——CPU suspend/resume流程_disabling non-boot cpus-CSDN博客
4 挂起到disk (STR) 和 唤醒
4.1 简介
将系统状态全部保存到硬盘以后,切断电源。功耗跟软关机相同 (只有电源适配器等部件供电)。唤醒时间通常需要1分钟以上。
《⽤“芯”探核:基于⻰芯的Linux内核探索解析》 8.1 电源管理概述
4.2 触发挂起到disk
echo disk > /sys/power/state
/sys/power/disk
如果往/sys/power/disk中写入了platform,会调用hibernation_platform_enter()进入真正意义上的ACPI S4睡眠状态;如果往/sys/power/disk中写入了shutdown,会调用kernel_power_off()关机;如果往/sys/power/disk中写入了reboot,会调用kernel_restart()重启;如果往/sys/power/disk中写入了suspend,会调用suspend_devices_and_enter()进入STR状态。
《⽤“芯”探核:基于⻰芯的Linux内核探索解析》 8.3.2 睡眠到磁盘 (休眠)
4.3 代码大致流程
hibernate();
-> __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, ...);
-> freeze_processes();
-> hibernation_snapshot();
-> create_image(); //Create a hibernation image.
-> dpm_suspend_end();
-> suspend_disable_secondary_cpus();
-> freeze_secondary_cpus();
-> trace_suspend_resume(TPS("CPU_OFF"), cpu, true);
-> _cpu_down(cpu, 1, CPUHP_OFFLINE);
-> trace_suspend_resume(TPS("CPU_OFF"), cpu, false);
-> syscore_suspend();
-> trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, true);
-> swsusp_arch_suspend();
-> trace_suspend_resume(TPS("machine_suspend"), PM_EVENT_HIBERNATE, false);
-> syscore_resume();
-> local_irq_enable();
-> suspend_enable_secondary_cpus();
-> enable_nonboot_cpus();
-> trace_suspend_resume(TPS("CPU_ON"), cpu, true);
-> _cpu_up(cpu, 1, CPUHP_ONLINE);
-> trace_suspend_resume(TPS("CPU_ON"), cpu, false);
-> dpm_resume(msg);
-> trace_suspend_resume(TPS("dpm_resume"), state.event, true);
-> device_resume();
-> cpufreq_resume();
-> devfreq_resume();
-> trace_suspend_resume(TPS("dpm_resume"), state.event, false);
-> dpm_complete();
-> trace_suspend_resume(TPS("dpm_complete"), state.event, true);
-> device_complete();
-> trace_suspend_resume(TPS("dpm_complete"), state.event, false);
-> pm_pr_dbg("Writing image.\n");
-> power_down();
-> thaw_processes();
-> __pm_notifier_call_chain(PM_POST_HIBERNATION, ...);
4.4 查看流程信息
echo shutdown > /sys/power/disk
# echo 1 > /sys/kernel/debug/tracing/events/power/suspend_resume/enable
# cat /sys/kernel/debug/tracing/trace_pipe (然后在另外一个终端运行:echo disk > /sys/power/state)
bash-3926 [000] ..... 67.704184: suspend_resume: dpm_prepare[1] begin
bash-3926 [000] ..... 67.704353: suspend_resume: dpm_prepare[1] end
bash-3926 [000] ..... 67.704354: suspend_resume: dpm_suspend[1] begin
bash-3926 [004] ..... 68.208620: suspend_resume: dpm_suspend[1] end
bash-3926 [004] ..... 68.208622: suspend_resume: dpm_suspend_late[1] begin
bash-3926 [004] ..... 68.229062: suspend_resume: dpm_suspend_late[1] end
bash-3926 [004] ..... 68.229425: suspend_resume: dpm_suspend_noirq[1] begin
bash-3926 [004] ..... 68.258933: suspend_resume: dpm_suspend_noirq[1] end
bash-3926 [004] ..... 68.297276: suspend_resume: CPU_OFF[1] begin
bash-3926 [000] ..... 68.299200: suspend_resume: CPU_OFF[1] end
bash-3926 [000] ..... 68.299201: suspend_resume: CPU_OFF[2] begin
bash-3926 [000] ..... 68.301045: suspend_resume: CPU_OFF[2] end
bash-3926 [000] ..... 68.301045: suspend_resume: CPU_OFF[3] begin
bash-3926 [000] ..... 68.302799: suspend_resume: CPU_OFF[3] end
bash-3926 [000] ..... 68.302800: suspend_resume: CPU_OFF[4] begin
bash-3926 [000] ..... 68.305293: suspend_resume: CPU_OFF[4] end
bash-3926 [000] ..... 68.305294: suspend_resume: CPU_OFF[5] begin
bash-3926 [000] ..... 68.306920: suspend_resume: CPU_OFF[5] end
bash-3926 [000] ..... 68.306921: suspend_resume: CPU_OFF[6] begin
bash-3926 [000] ..... 68.308614: suspend_resume: CPU_OFF[6] end
bash-3926 [000] ..... 68.308615: suspend_resume: CPU_OFF[7] begin
bash-3926 [000] ..... 68.310275: suspend_resume: CPU_OFF[7] end
bash-3926 [000] d.... 68.310276: suspend_resume: syscore_suspend[0] begin
bash-3926 [000] d.... 68.314313: suspend_resume: syscore_suspend[0] end
bash-3926 [000] d.... 68.314316: suspend_resume: machine_suspend[4] begin
bash-3926 [000] d.... 68.314591: suspend_resume: machine_suspend[4] end
bash-3926 [000] d.... 68.315843: suspend_resume: syscore_resume[0] begin
bash-3926 [000] dN... 68.317340: suspend_resume: syscore_resume[0] end
bash-3926 [000] ..... 68.317355: suspend_resume: CPU_ON[1] begin
bash-3926 [000] ..... 68.318497: suspend_resume: CPU_ON[1] end
bash-3926 [000] ..... 68.318501: suspend_resume: CPU_ON[2] begin
bash-3926 [000] ..... 68.319797: suspend_resume: CPU_ON[2] end
bash-3926 [000] ..... 68.319799: suspend_resume: CPU_ON[3] begin
bash-3926 [000] ..... 68.321175: suspend_resume: CPU_ON[3] end
bash-3926 [000] ..... 68.321178: suspend_resume: CPU_ON[4] begin
bash-3926 [000] ..... 68.322554: suspend_resume: CPU_ON[4] end
bash-3926 [000] ..... 68.322557: suspend_resume: CPU_ON[5] begin
bash-3926 [000] ..... 68.323762: suspend_resume: CPU_ON[5] end
bash-3926 [000] ..... 68.323764: suspend_resume: CPU_ON[6] begin
bash-3926 [000] ..... 68.325180: suspend_resume: CPU_ON[6] end
bash-3926 [000] ..... 68.325182: suspend_resume: CPU_ON[7] begin
bash-3926 [000] ..... 68.326482: suspend_resume: CPU_ON[7] end
bash-3926 [000] ..... 68.378249: suspend_resume: dpm_resume_noirq[64] begin
bash-3926 [001] ..... 68.410735: suspend_resume: dpm_resume_noirq[64] end
bash-3926 [001] ..... 68.410766: suspend_resume: dpm_resume_early[64] begin
bash-3926 [001] ..... 68.411873: suspend_resume: dpm_resume_early[64] end
bash-3926 [001] ..... 68.478412: suspend_resume: dpm_resume[64] begin
bash-3926 [005] ..... 71.226730: suspend_resume: dpm_resume[64] end
bash-3926 [005] ..... 71.226739: suspend_resume: dpm_complete[64] begin
bash-3926 [005] ..... 71.234815: suspend_resume: dpm_complete[64] end
bash-3926 [005] ..... 71.234895: suspend_resume: thaw_processes[0] begin
bash-3926 [001] ..... 71.237957: suspend_resume: thaw_processes[0] end
4.5 swsusp
4.5.1 简介
2.6内核实现了一种叫做swsusp的STD方法。
swsusp saves the state of the machine into active swaps and then reboots or
powerdowns. You must explicitly specify the swap partition to resume from with
`resume=` kernel option. If signature is found it loads and restores saved
state. If the option `noresume` is specified as a boot parameter, it skips
the resuming. If the option `hibernate=nocompress` is specified as a boot
parameter, it saves hibernation image without compression.
Documentation/power/swsusp.rst
4.5.2 配置swsusp参数
方法一、启动参数:
resume=<swap_file_partition> resume_offset=<swap_file_offset>
方法二、配置文件:
/sys/power/resume_offset
/sys/power/resume
Documentation/power/swsusp-and-swap-files.rst
4.6 相关的启动参数:resumedelay= | resumewait | hibernate=
resumedelay= [HIBERNATION] Delay (in seconds) to pause before attempting to
read the resume files
resumewait [HIBERNATION] Wait (indefinitely) for resume device to show up.
Useful for devices that are detected asynchronously
(e.g. USB and MMC devices).
hibernate= [HIBERNATION]
noresume Don't check if there's a hibernation image
present during boot.
nocompress Don't compress/decompress hibernation images.
no Disable hibernation and resume.
protect_image Turn on image protection during restoration
(that will set all pages holding image data
during restoration read-only).
Documentation/admin-guide/kernel-parameters.txt
5 notifier:pm_chain_head
5.1 简介
当期望在低功耗睡眠/唤醒事件发生时执行指定的处理函数时,可以通过调用register_pm_notifier注册到pm_chain_head来实现。
《SoC底层软件低功耗系统设计与实现》5.1.6 内核使用场景
5.2 代码
数据结构:
static BLOCKING_NOTIFIER_HEAD(pm_chain_head);
注册接口:
register_pm_notifier();
-> blocking_notifier_chain_register(&pm_chain_head, nb);
发出通知的接口:
__pm_notifier_call_chain();
-> __blocking_notifier_call_chain();
pm_notifier_call_chain();
-> __pm_notifier_call_chain();
5.3 在suspend时发出通知
pm_suspend();
-> enter_state();
-> suspend_prepare();
-> __pm_notifier_call_chain(PM_SUSPEND_PREPARE, ...);
......
-> suspend_finish();
-> suspend_thaw_processes();
-> pm_notifier_call_chain(PM_POST_SUSPEND);
5.4 在hibernate时发出通知
hibernate();
-> __pm_notifier_call_chain(PM_HIBERNATION_PREPARE, ...);
......
-> __pm_notifier_call_chain(PM_POST_HIBERNATION, ...);
6 调试
6.1 查看统计信息:/sys/power/suspend_stats/
/sys/power/suspend_stats/目录下包含以下文件:
# ls -l /sys/power/suspend_stats
总用量 0
-r--r--r-- 1 root root 4096 4月 5 14:56 fail
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_freeze
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_prepare
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_resume
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_resume_early
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_resume_noirq
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_suspend
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_suspend_late
-r--r--r-- 1 root root 4096 4月 5 14:56 failed_suspend_noirq
-r--r--r-- 1 root root 4096 4月 5 14:56 last_failed_dev
-r--r--r-- 1 root root 4096 4月 5 14:56 last_failed_errno
-r--r--r-- 1 root root 4096 4月 5 14:56 last_failed_step
-r--r--r-- 1 root root 4096 4月 5 14:56 success
文件内容含义如下:
success: contains the number of times entering system sleep state succeeded.
fail: contains the number of times entering system sleep state failed.
failed_freeze: contains the number of times freezing processes failed.
failed_prepare: contains the number of times preparing all non-sysdev devices for
a system PM transition failed.
failed_suspend: contains the number of times executing "suspend" callbacks
of all non-sysdev devices failed.
failed_suspend_late: contains the number of times executing "late suspend" callbacks
of all devices failed.
failed_suspend_noirq: contains the number of times executing "noirq suspend" callbacks
of all devices failed.
failed_resume: contains the number of times executing "resume" callbacks
of non-sysdev devices failed.
failed_resume_early: contains the number of times executing "early resume" callbacks
of devices failed.
failed_resume_noirq: contains the number of times executing "noirq resume" callbacks
of devices failed.
last_failed_errno: contains the errno of the last failed attempt at entering system sleep state.
last_failed_dev: contains the last device for which a suspend/resume callback failed.
last_failed_step: contains the last failed step in the suspend/resume path.
Documentation/ABI/testing/sysfs-power
6.2 console_suspend
如果想在挂起和恢复的过程中,看到内核的打印信息以关注具体的详细流程, 可以在Bootloader传递给内核的bootargs中设置标志no_console_suspend。
设置⽅法
方法一、启动参数:no_console_suspend
方法二、/sys/module/printk/parameters/console_suspend
Documentation/admin-guide/kernel-parameters.txt