Suspend to RAM和Suspend to Idle分析,以及在HiKey上性能对比

Posted Arnold Lu@南京

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Suspend to RAM和Suspend to Idle分析,以及在HiKey上性能对比相关的知识,希望对你有一定的参考价值。

测试环境:AOSP 7.1.1+Kernel 4.4.17 HW:HiKey

             Ubuntu 14.04+Kernel 4.4.0-31

联系方式:arnoldlu@qq.com

1. Linux内核suspend状态

Linux内核支持多种类型的睡眠状态,通过设置不同的模块进入低功耗模式来达到省电功能。

目前存在四种模式:suspend to idle、power-on standby(Standby)、suspend to ram(STR)和sudpend to disk(Hibernate),分别对应ACPI状态的S0、S1、S3和S4。

State in Linux Label state ACPI state 注释
#define PM_SUSPEND_ON        ((__force suspend_state_t) 0)        一切正常
#define PM_SUSPEND_FREEZE    ((__force suspend_state_t) 1) freeze Suspend-to-Idle S0 冻结进程+挂起设备+CPU空闲 
#define PM_SUSPEND_STANDBY    ((__force suspend_state_t) 2) standby Standby/Power-on Suspend S1 冻结进程+挂起设备+关闭nonbootCPU 
#define PM_SUSPEND_MEM        ((__force suspend_state_t) 3) mem Suspend-to-RAM S3 仅保留RAM自刷新 
#define PM_SUSPEND_MAX        ((__force suspend_state_t) 4) disk Suspend-to-disk S4

关闭所有设备包括RAM也被称为Hibernate

从freeze-->standby-->mem睡眠程度越来越深,唤醒花费的时间也越来越多。

Suspend-To-Idle

此状态包括frozen processes+suspended devices+idle processors,具有轻量化的特点;

并且相对于相对于Idle状态能节省更多的功耗,因为此时的用户空间被冻结且I/O设备进入了低功耗状态。

相对于Suspend-To-RAM它具有低延时的优势。

Standby/Power-On Suspend

此状态包括frozen processes+suspended devices+offline nonboot CPUs+suspend low-level system,对CPU的处理更近一步。

所以相对于Suspend-To-Idle节省了更多的功耗,但是由于需要恢复CPU和一些底层功能也花费了更多的时间。

Suspend-to-RAM

此状态使所有的设备进入低功耗状态,仅保留RAM自刷新。

所有的设备和系统状态都保存在RAM中,所有外设被挂起。

在HiKey的实际测试中,boot CPU是没有关闭的!实际上这里也没有standby,mem和standby基本上没有区别。

Suspend-to-disk

此状态是最省功耗的模式。

相对Suspend-to-RAMRAM能节省更多功耗的原因是数据会被写入磁盘中,RAM也可以被关闭。

但是这也导致了,更多的恢复延时,在resume的时候读回到RAM,然后在进行系统和设备状态恢复工作。

但是在一般的嵌入式设备上,此种状态不支持。

 

下面用STR表示Suspend to RAM,STI表示Suspend to Idle。

详情请参考:http://www.linaro.org/blog/suspend-to-idle/

2. Suspend状态,以及STR 和STI区别

写入/sys/power/state不同字符串,可以让系统进入不同睡眠状态。

针对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.
     */
//====================================FREEZE===============================================================
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的操作。 } //=====================================MEM=============================================================== 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); … }

 

3 suspend/resume流程梳理

下面分析一下suspend/resume每个细分阶段。

整个suspend可以分为若干阶段,每个阶段函数—>关键节点Trace—>analyze_suspend.py解析Trace—>根据Trace时间画出Timeline图表

这样就可以分析出总的时间差异,每个阶段差异,甚至一个设备suspend/resume、一个子系统suspend/resume的时间差异。

analyze_suspend.py 基于默认基于ftrace进行分析(在指定dmesg的时候,会发现缺失了很多log信息,无法生成timeline类型的html文件),将suspend/resume分为若干阶段。

下面简要介绍一下各个阶段,然后基于此进行代码分析。

在kernel版本大于等与3.15之后,解析需要的所有log信息都可以从ftrace中获取。之前的内核版本还需要借助于dmesg。

由于使用的kernel版本是4.4.17,sysvals.usetraceeventsonly被置位,所以只会parseTraceLog()。

下表中的各个阶段通过解析suspend_resume: XXXXXXX类型的ftrace来获取。

各子模块、子系统的解析通过device_pm_callback_start和device_pm_callback_end来截取时间段,以及这时间段内的callgraph。

Phase名称 ftrace关键词    
suspend_prepare dpm_prepare    
suspend dpm_suspend    
suspend_late dpm_suspend_late    
suspend_noirq dpm_suspend_noirq    
suspend_machine machine_suspend start    
resume_machine machine_suspend end    
resume_noirq dpm_resume_noirq    
resume_early dpm_resume_early    
resume dpm_resume    
resume_complete dpm_complete    

下面是一组suspend/resume执行ftrace log,我们将据此进行各阶段代码分析,包括suspend_enter、suspend_prepare、suspend、suspend_late、suspend_noirq、suspend_machine、resume_machine、resume_noirq、resume_early、resume、resume_complete。

从这里也可以看出freeze和mem/standby除了machine部分不同之外,还少了CPU开关和syscore suspend/resume操作。

suspend_resume: suspend_enter[1] begin
suspend_resume: sync_filesystems[0] begin
suspend_resume: sync_filesystems[0] end
suspend_resume: freeze_processes[0] begin
suspend_resume: freeze_processes[0] end
suspend_resume: suspend_enter[1] end
suspend_resume: dpm_prepare[2] begin
suspend_resume: dpm_prepare[2] end
suspend_resume: dpm_suspend[2] begin
suspend_resume: dpm_suspend[2] end
suspend_resume: dpm_suspend_late[2] begin
suspend_resume: dpm_suspend_late[2] end
suspend_resume: dpm_suspend_noirq[2] begin
suspend_resume: dpm_suspend_noirq[2] end
No CPU_OFF…
No syscore_suspend…

suspend_resume: machine_suspend[1] begin
suspend_resume: machine_suspend[1] end
No suscore_resume…
No CPU_ON…

suspend_resume: dpm_resume_noirq[16] begin
suspend_resume: dpm_resume_noirq[16] end
suspend_resume: dpm_resume_early[16] begin
suspend_resume: dpm_resume_early[16] end
suspend_resume: dpm_resume[16] begin
suspend_resume: dpm_resume[16] end
suspend_resume: dpm_complete[16] begin
suspend_resume: dpm_complete[16] end
suspend_resume: resume_console[1] begin
suspend_resume: resume_console[1] end
suspend_resume: thaw_processes[0] begin
suspend_resume: thaw_processes[0] end

suspend_resume: suspend_enter[3] begin
suspend_resume: sync_filesystems[0] begin
suspend_resume: sync_filesystems[0] end
suspend_resume: freeze_processes[0] begin
suspend_resume: freeze_processes[0] end
suspend_resume: suspend_enter[3] end
suspend_resume: dpm_prepare[2] begin
suspend_resume: dpm_prepare[2] end
suspend_resume: dpm_suspend[2] begin
suspend_resume: dpm_suspend[2] end
suspend_resume: dpm_suspend_late[2] begin
suspend_resume: dpm_suspend_late[2] end
suspend_resume: dpm_suspend_noirq[2] begin
suspend_resume: dpm_suspend_noirq[2] end
suspend_resume: CPU_OFF[1-7] begin/end
suspend_resume: syscore_suspend[0] begin/end
suspend_resume: machine_suspend[3] begin
suspend_resume: machine_suspend[3] end
suspend_resume: syscore_resume[0] begin/end
suspend_resume: CPU_ON[1-7] begin/end
suspend_resume: dpm_resume_noirq[16] begin
suspend_resume: dpm_resume_noirq[16] end
suspend_resume: dpm_resume_early[16] begin
suspend_resume: dpm_resume_early[16] end
suspend_resume: dpm_resume[16] begin
suspend_resume: dpm_resume[16] end
suspend_resume: dpm_complete[16] begin
suspend_resume: dpm_complete[16] end
suspend_resume: resume_console[3] begin
suspend_resume: resume_console[3] end
suspend_resume: thaw_processes[0] begin
suspend_resume: thaw_processes[0] end

在介绍相关代码之前,先介绍一下HiKey使用的platform_suspend_ops:

static const struct platform_suspend_ops psci_suspend_ops = {
    .valid          = suspend_valid_only_mem,  仅支持mem类型的suspend
    .enter          = psci_system_suspend_enter,  睡眠的CPU底层支持
};

freeze的platform_freeze_ops如下:

static const struct platform_freeze_ops acpi_freeze_ops = {
    .begin = acpi_freeze_begin,
    .prepare = acpi_freeze_prepare,
    .restore = acpi_freeze_restore,
    .end = acpi_freeze_end,
};

3.1 suspend_enter

enter_state作为suspend/resume的入口点,完成了绝大部分工作。首先确保系统没有正在进入睡眠状态;然后为suspend做一些准备,使系统进入睡眠并在唤醒后进行必要清理恢复工作。

下面分析一下suspend之前的准备工作,即suspend_enter阶段:

static int enter_state(suspend_state_t state)
{
    int error;

    trace_suspend_resume(TPS("suspend_enter"), state, true);
    if (state == PM_SUSPEND_FREEZE) {--------------------------------------是否是freeze类型suspend
#ifdef CONFIG_PM_DEBUG
        if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
            pr_warning("PM: Unsupported test mode for suspend to idle,"
                   "please choose none/freezer/devices/platform.\\n");
            return -EAGAIN;
        }
#endif
    } else if (!valid_state(state)) {-------------------------------------目前只支持mem类型suspend
        return -EINVAL;
    }
    if (!mutex_trylock(&pm_mutex))
        return -EBUSY;

    if (state == PM_SUSPEND_FREEZE)
        freeze_begin();--------------------------------------------------初始化suspend_freeze_state为FREEZE_STATE_NONE

#ifndef CONFIG_SUSPEND_SKIP_SYNC
    trace_suspend_resume(TPS("sync_filesystems"), 0, true);
    printk(KERN_INFO "PM: Syncing filesystems ... ");
    sys_sync();----------------------------------------------------------sync文件系统缓存文件,确保数据sync到硬盘。
    printk("done.\\n");
    trace_suspend_resume(TPS("sync_filesystems"), 0, false);
#endif

    pr_debug("PM: Preparing system for sleep (%s)\\n", pm_states[state]);
    pm_suspend_clear_flags();
    error = suspend_prepare(state);--------------------------------------注意这里面的suspend_prepare和下面的suspend_prepare阶段容易搞混。
    if (error)
        goto Unlock;

    if (suspend_test(TEST_FREEZER))
        goto Finish;

    trace_suspend_resume(TPS("suspend_enter"), state, false);
    pr_debug("PM: Suspending system (%s)\\n", pm_states[state]);
    pm_restrict_gfp_mask();
    error = suspend_devices_and_enter(state);
    pm_restore_gfp_mask();

Finish:
    pr_debug("PM: Finishing wakeup.\\n");
    suspend_finish();---------------------------------------------------解冻,重启进程;发送PM_POST_SUSPEND通知;释放之前分配的console。
Unlock:
    mutex_unlock(&pm_mutex);
    return error;
}

 

接着分析一下suspend_prepare函数:

static int suspend_prepare(suspend_state_t state)
{
    int error;

    if (!sleep_state_supported(state))  验证suspend状态
        return -EPERM;

    pm_prepare_console();  分配一个suspend console

    error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);  发送PM_SUSPEND_PREPARE通知消息
    if (error)
        goto Finish;

    trace_suspend_resume(TPS("freeze_processes"), 0, true);
    error = suspend_freeze_processes();  冻结进程
    trace_suspend_resume(TPS("freeze_processes"), 0, false);
    if (!error)
        return 0;

    suspend_stats.failed_freeze++;
    dpm_save_failed_step(SUSPEND_FREEZE);
Finish:
    pm_notifier_call_chain(PM_POST_SUSPEND);
    pm_restore_console();
    return error;
}

suspend_freeze_process先处理用户空间进程,然后处理内核进程:

static inline int suspend_freeze_processes(void)
{
    int error;

    error = freeze_processes();  触发用户空间进程进入freeze状态。当前进程不会被冻结。因为冻结失败的进程会自动被解冻,所以不需要进行错误处理。
    /*
     * freeze_processes() automatically thaws every task if freezing
     * fails. So we need not do anything extra upon error.
     */
    if (error)
        return error;

    error = freeze_kernel_threads();  冻结内核线程
    /*
     * freeze_kernel_threads() thaws only kernel threads upon freezing
     * failure. So we have to thaw the userspace tasks ourselves.
     */
    if (error)  由于freeze_kernel_threads冻结失败,只会解冻内核线程。所以还需要对用户空间进程进行解冻。
        thaw_processes();

    return error;
}

 

下面的阶段都在suspend_devices_and_enter中,可以看出这是一个对称的流程,每一阶段的suspend,都有对应的resume。

int suspend_devices_and_enter(suspend_state_t state)
{
    int error;
    bool wakeup = false;

    if (!sleep_state_supported(state))
        return -ENOSYS;

    error = platform_suspend_begin(state);
    if (error)
        goto Close;

    suspend_console();  关闭console子系统,暂停printk打印
    suspend_test_start();
    error = dpm_suspend_start(PMSG_SUSPEND); suspend_prepare(dpm_prepare)、suspend(dpm_suspend)两阶段
    if (error) {
        pr_err("PM: Some devices failed to suspend, or early wake event detected\\n");
        log_suspend_abort_reason("Some devices failed to suspend, or early wake event detected");
        goto Recover_platform;
    }
    suspend_test_finish("suspend devices");
    if (suspend_test(TEST_DEVICES))
        goto Recover_platform;

    do {
        error = suspend_enter(state, &wakeup);  suspend_late(dpm_suspend_late)、suspend_noirq(dpm_suspend_noirq)、suspend_machine、resume_machine、resume_noirq(dpm_resume_noirq)、resume_early(dpm_resume_early)
    } while (!error && !wakeup && platform_suspend_again(state));

Resume_devices:
    suspend_test_start();
    dpm_resume_end(PMSG_RESUME);  resume(dpm_resume)、resume_complete(dpm_complete)
    suspend_test_finish("resume devices");
    trace_suspend_resume(TPS("resume_console"), state, true);
    resume_console();  打开console子系统,恢复printk打印。
    trace_suspend_resume(TPS("resume_console"), state, false);

Close:
    platform_resume_end(state);
    return error;

Recover_platform:
    platform_recover(state);
    goto Resume_devices;
}

还有必要过一下suspend_enter:

static int suspend_enter(suspend_state_t state, bool *wakeup)
{
    char suspend_abort[MAX_SUSPEND_ABORT_LEN];
    int error, last_dev;

    error = platform_suspend_prepare(state); 因为suspend_ops的prepare为空,所以返回0
    if (error)
        goto Platform_finish;

    error = dpm_suspend_late(PMSG_SUSPEND);  suspend_late
    if (error) {
        last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
        last_dev %= REC_FAILED_NUM;
        printk(KERN_ERR "PM: late suspend of devices failed\\n");
        log_suspend_abort_reason("%s device failed to power down",
            suspend_stats.failed_devs[last_dev]);
        goto Platform_finish;
    }
    error = platform_suspend_prepare_late(state);  执行freeze_ops->prepare()
    if (error)
        goto Devices_early_resume;

    error = dpm_suspend_noirq(PMSG_SUSPEND);  suspend_noirq
    if (error) {
        last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
        last_dev %= REC_FAILED_NUM;
        printk(KERN_ERR "PM: noirq suspend of devices failed\\n");
        log_suspend_abort_reason("noirq suspend of %s device failed",
            suspend_stats.failed_devs[last_dev]);
        goto Platform_early_resume;
    }
    error = platform_suspend_prepare_noirq(state);
    if (error)
        goto Platform_wake;

    if (suspend_test(TEST_PLATFORM))
        goto Platform_wake;

    /*
     * 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和mem/standy差别所在
        trace_suspend_resume(TPS("machine_suspend"), state, true);
        freeze_enter(); 
        trace_suspend_resume(TPS("machine_suspend"), state, false);
        goto Platform_wake;
    }

    error = disable_nonboot_cpus(); 关闭所有boot-CPU之外的CPU
    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();  执行syscore_ops_list上所有syscore_ops的suspend回调函数
    if (!error) {
        *wakeup = pm_wakeup_pending();  检查是否需要终止suspend流程?
        if (!(suspend_test(TEST_CORE) || *wakeup)) {
            trace_suspend_resume(TPS("machine_suspend"),
                state, true);
            error = suspend_ops->enter(state);  调用psci_suspend_ops的enter回调函数,关闭machine
            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();  执行所有syscore_ops_list的resume回调函数
    }

    arch_suspend_enable_irqs();
    BUG_ON(irqs_disabled());

Enable_cpus:
    enable_nonboot_cpus();  打开所有non-boot CPU

Platform_wake:
    platform_resume_noirq(state);
    dpm_resume_noirq(PMSG_RESUME);  resume_noirq

Platform_early_resume:
    platform_resume_early(state);

Devices_early_resume:
    dpm_resume_early(PMSG_RESUME);  resume_early

Platform_finish:
    platform_resume_finish(state);
    return error;
}

3.2 suspend_prepare和suspend

DPM是Device Power Management的意思,这些操作都是针对非系统设备(non-sysdev)进行的。那什么是系统设备呢?下面的machine应该就是所谓的sysdev了。

dpm_prepare实际上就是遍历dpm_list上的所有设备,执行->prepare回调函数。如果设备存在->prepare回电函数,会将设备的prepare阶段打印到ftrace。

int dpm_prepare(pm_message_t state)
{
    int error = 0;

    trace_suspend_resume(TPS("dpm_prepare"), state.event, true);
    might_sleep();

    mutex_lock(&dpm_list_mtx);
    while (!list_empty(&dpm_list)) {  遍历dpm_list
        struct device *dev = to_device(dpm_list.next);

        get_device(dev);
        mutex_unlock(&dpm_list_mtx);

        trace_device_pm_callback_start(dev, "", state.event);
        error = device_prepare(dev, state);  执行->prepare回调函数
        trace_device_pm_callback_end(dev, error);

        mutex_lock(&dpm_list_mtx);
        if (error) {
            if (error == -EAGAIN) {
                put_device(dev);
                error = 0;
                continue;
            }
            printk(KERN_INFO "PM: Device %s not prepared "
                "for power transition: code %d\\n",
                dev_name(dev), error);
            put_device(dev);
            break;
        }
        dev->power.is_prepared = true;
        if (!list_empty(&dev->power.entry))
            list_move_tail(&dev->power.entry, &dpm_prepared_list);  移动设备到dpm_prepared_list
        put_device(dev);
    }
    mutex_unlock(&dpm_list_mtx);
    trace_suspend_resume(TPS("dpm_prepare"), state.event, false);
    return error;
}

dpm_suspend遍历dpm_prepared_list,这点和dpm_prepare有区别。然后执行设备的->suspend回调函数。

int dpm_suspend(pm_message_t state)
{
    ktime_t starttime = ktime_get();
    int error = 0;

    trace_suspend_resume(TPS("dpm_suspend"), state.event, true);
    might_sleep();

    cpufreq_suspend();

    mutex_lock(&dpm_list_mtx);
    pm_transition = state;
    async_error = 0;
    while (!list_empty(&dpm_prepared_list)) { 基于dpm_prepared_list遍历设备
        struct device *dev = to_device(dpm_prepared_list.prev);

        get_device(dev);
        mutex_unlock(&dpm_list_mtx);

        error = device_suspend(dev);  执行设备->suspend回调函数

        mutex_lock(&dpm_list_mtx);
        if (error) {
            pm_dev_err(dev, state, "", error);
            dpm_save_failed_dev(dev_name(dev));
            put_device(dev);
            break;
        }
        if (!list_empty(&dev->power.entry))
            list_move(&dev->power.entry, &dpm_suspended_list);  移动设备到dpm_suspended_list
        put_device(dev);
        if (async_error)
            break;
    }
    mutex_unlock(&dpm_list_mtx);
    async_synchronize_full();
    if (!error)
        error = async_error;
    if (error) {
        suspend_stats.failed_suspend++;
        dpm_save_failed_step(SUSPEND_SUSPEND);
    } else
        dpm_show_time(starttime, state, NULL);
    trace_suspend_resume(TPS("dpm_suspend"), state.event, false);
    return error;
}

3.3 suspend_late和suspend_noirq

dpm_suspend_late基于dpm_suspended_list操作设备,所以这也需要函数之间顺序执行。

int dpm_suspend_late(pm_message_t state)
{
    ktime_t starttime = ktime_get();
    int error = 0;

    trace_suspend_resume(TPS("dpm_suspend_late"), state.event, true);
    mutex_lock(&dpm_list_mtx);
    pm_transition = state;
    async_error = 0;

    while (!list_empty(&dpm_suspended_list)) {  遍历dpm_suspended_list列表
        struct device *dev = to_device(dpm_suspended_list.prev);

        get_device(dev);
        mutex_unlock(&dpm_list_mtx);

        error = device_suspend_late(dev);  执行->suspend_late回调函数

        mutex_lock(&dpm_list_mtx);
        if (!list_empty(&dev->power.entry))
            list_move(&dev->power.entry, &dpm_late_early_list);  移动设备到dpm_late_early_list

        if (error) {
            pm_dev_err(dev, state, " late", error);
            dpm_save_failed_dev(dev_name(dev));
            put_device(dev);
            break;
        }
        put_device(dev);

        if (async_error)
            break;
    }
    mutex_unlock(&dpm_list_mtx);
    async_synchronize_full();
    if (!error)
        error = async_error;
    if (error) {
        suspend_stats.failed_suspend_late++;
        dpm_save_failed_step(SUSPEND_SUSPEND_LATE);
        dpm_resume_early(resume_event(state));
    } else {
        dpm_show_time(starttime, state, "late");
    }
    trace_suspend_resume(TPS("dpm_suspend_late"), state.event, false);
    return error;
}

dpm_suspend_noirq基于dpm_late_early_list遍历所有设备。首先阻止设备驱动接收中断信息,然后执行->suspend_noirq回调函数。

int dpm_suspend_noirq(pm_message_t state)
{
    ktime_t starttime = ktime_get();
    int error = 0;

    trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);
    cpuidle_pause();  暂停cpuidle功能,退出idle的CPU
    device_wakeup_arm_wake_irqs();  将具有wakeirq的设备设置成wakeup resource
    suspend_device_irqs();  关闭当前所有能够关闭的irq,置成IRQS_SUSPENDED。IRQF_NO_SUSPEND类型的wakeup中断不能被关闭,并且作为wakeup唤醒源的中断不能被关闭。
    mutex_lock(&dpm_list_mtx);
    pm_transition = state;
    async_error = 0;

    while (!list_empty(&dpm_late_early_list)) {
        struct device *dev = to_device(dpm_late_early_list.prev);

        get_device(dev);
        mutex_unlock(&dpm_list_mtx);

        error = device_suspend_noirq(dev);  调用->suspend_noirq回调函数

        mutex_lock(&dpm_list_mtx);
        if (error) {
            pm_dev_err(dev, state, " noirq", error);
            dpm_save_failed_dev(dev_name(dev));
            put_device(dev);
            break;
        }
        if (!list_empty(&dev->power.entry))
            list_move(&dev->power.entry, &dpm_noirq_list);  移动设备到dpm_noirq_list
        put_device(dev);

        if (async_error)
            break;
    }
    mutex_unlock(&dpm_list_mtx);
    async_synchronize_full();
    if (!error)
        error = async_error;

    if (error) {
        suspend_stats.failed_suspend_noirq++;
        dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);
        dpm_resume_noirq(resume_event(state));
    } else {
        dpm_show_time(starttime, state, "noirq");
    }
    trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false);
    return error;
}

3.4 suspend_machine和resume_machine

freeze和mem/standby在这部分是不同的。

mem/standby直接调用suspend_ops->enter进入对应的睡眠模式。

而freeze就要稍微复杂了:

static void freeze_enter(void)
{
    spin_lock_irq(&suspend_freeze_lock);
    if (pm_wakeup_pending())  检查是否有wakeup信号在处理,如果有则退出当前流程。
        goto out;

    suspend_freeze_state = FREEZE_STATE_ENTER;
    spin_unlock_irq(&suspend_freeze_lock);

    get_online_cpus();
    cpuidle_resume();  允许使用cpuidle

    /* Push all the CPUs into the idle loop. */
    wake_up_all_idle_cpus();  强制所有CPU退出idle状态
    pr_debug("PM: suspend-to-idle\\n");
    /* Make the current CPU wait so it can enter the idle loop too. */
    wait_event(suspend_freeze_wait_head,
           suspend_freeze_state == FREEZE_STATE_WAKE);  等待FREEZE_STATE_WAKE事件,进入idle loop
    pr_debug("PM: resume from suspend-to-idle\\n");  !!!!!!!!!!!!!!!!这里即为唤醒之后的执行路径了!!!!!!!!!!!!!!!!
    cpuidle_pause();  暂停使用cpuidle
    put_online_cpus();

    spin_lock_irq(&suspend_freeze_lock);

out:
    suspend_freeze_state = FREEZE_STATE_NONE;
    spin_unlock_irq(&suspend_freeze_lock);
}

 

3.5 resume_noirq

执行dpm_noirq_list上设备的resume_noirq回调函数。

void dpm_resume_noirq(pm_message_t state)
{
    struct device *dev;
    ktime_t starttime = ktime_get();

    trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true);
    mutex_lock(&dpm_list_mtx);
    pm_transition = state;

    /*
     * Advanced the async threads upfront,
     * in case the starting of async threads is
     * delayed by non-async resuming devices.
     */
    list_for_each_entry(dev, &dpm_noirq_list, power.entry) {
        reinit_completion(&dev->power.completion);
        if (is_async(dev)) {
            get_device(dev);
            async_schedule(async_resume_noirq, dev);
        }
    }

    while (!list_empty(&dpm_noirq_list)) {  遍历dpm_noirq_list
        dev = to_device(dpm_noirq_list.next);
        get_device(dev);
        list_move_tail(&dev->power.entry, &dpm_late_early_list);  移动设备到下一级dpm_late_early_list
        mutex_unlock(&dpm_list_mtx);

        if (!is_async(dev)) {
            int error;

            error = device_resume_noirq(dev, state, false);
            if (error) {
                suspend_stats.failed_resume_noirq++;
                dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);
                dpm_save_failed_dev(dev_name(dev));
                pm_dev_err(dev, state, " noirq", error);
            }
        }

        mutex_lock(&dpm_list_mtx);
        put_device(dev);
    }
    mutex_unlock(&dpm_list_mtx);
    async_synchronize_full();
    dpm_show_time(starttime, state, "noirq");
    resume_device_irqs();
    device_wakeup_disarm_wake_irqs();
    cpuidle_resume();
    trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
}

3.6 resume_early

执行前述dpm_late_early_list设备的resume_early回调函数,移动设备到dpm_suspended_list列表。

void dpm_resume_early(pm_message_t state)
{
    struct device *dev;
    ktime_t starttime = ktime_get();

    trace_suspend_resume(TPS("dpm_resume_early"), state.event, true);
    mutex_lock(&dpm_list_mtx);
    pm_transition = state;

    /*
     * Advanced the async threads upfront,
     * in case the starting of async threads is
     * delayed by non-async resuming devices.
     */
    list_for_each_entry(dev, &dpm_late_early_list, power.entry) {
        reinit_completion(&dev->power.completion);
        if (is_async(dev)) {
            get_device(dev);
            async_schedule(async_resume_early, dev);
        }
    }

    while (!list_empty(&dpm_late_early_list)) {
        dev = to_device(dpm_late_early_list.next);
        get_device(dev);
        list_move_tail(&dev->power.entry, &dpm_suspended_list);
        mutex_unlock(&dpm_list_mtx);

        if (!is_async(dev)) {
            int error;

            error = device_resume_early(dev, state, false);
            if (error) {
                suspend_stats.failed_resume_early++;
                dpm_save_failed_step(SUSPEND_RESUME_EARLY);
                dpm_save_failed_dev(dev_name(dev));
                pm_dev_err(dev, state, " early", error);
            }
        }
        mutex_lock(&dpm_list_mtx);
        put_device(dev);
    }
    mutex_unlock(&dpm_list_mtx);
    async_synchronize_full();
    dpm_show_time(starttime, state, "early");
    trace_suspend_resume(TPS("dpm_resume_early"), state.event, false);
}

3.7 resume

执行所有dpm_suspended_list上设备的resume回调函数。

void dpm_resume(pm_message_t state)
{
    struct device *dev;
    ktime_t starttime = ktime_get();

    trace_suspend_resume(TPS("dpm_resume"), state.event, true);
    might_sleep();

    mutex_lock(&dpm_list_mtx);
    pm_transition = state;
    async_error = 0;

    list_for_each_entry(dev, &dpm_suspended_list, power.entry) {
        reinit_completion(&dev->power.completion);
        if (is_async(dev)) {
            get_device(dev);
            async_schedule(async_resume, dev);
        }
    }

    while (!list_empty(&dpm_suspended_list)) {
        dev = to_device(dpm_suspended_list.next);
        get_device(dev);
        if (!is_async(dev)) {
            int error;

            mutex_unlock(&dpm_list_mtx);

            error = device_resume(dev, state, false);
            if (error) {
                suspend_stats.failed_resume++;
                dpm_save_failed_step(SUSPEND_RESUME);
                dpm_save_failed_dev(dev_name(dev));
                pm_dev_err(dev, state, "", error);
            }

            mutex_lock(&dpm_list_mtx);
        }
        if (!list_empty(&dev->power.entry))
            list_move_tail(&dev->power.entry, &dpm_prepared_list);
        put_device(dev);
    }
    mutex_unlock(&dpm_list_mtx);
    async_synchronize_full();
    dpm_show_time(starttime, state, NULL);

    cpufreq_resume();
    trace_suspend_resume(TPS("dpm_resume"), state.event, false);
}

3.8 resume_complete

执行所

以上是关于Suspend to RAM和Suspend to Idle分析,以及在HiKey上性能对比的主要内容,如果未能解决你的问题,请参考以下文章

Windows——Modern Standby(现代待机) S0改Suspend to RAM(待机到内存)S3睡眠解决方案(以机械革命F1 i5-11300H为例)

/sys/power/state

Linux 何时以及如何将 VGA 内存保存到 RAM?它在 pm-suspend 脚本中吗?或在 echo mem > /sys/power/state 之后的内核中

VxWorks中的任务状态 “pend”和“suspend”有啥区别

Java 学习笔记之 Suspend和Resume

Linux的suspend和resume