Linux电源管理(一),系统的待机、睡眠 和 唤醒

更多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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值