zoukankan      html  css  js  c++  java
  • Linux电源管理-Suspend/Resume流程【转】

    转自:https://blog.csdn.net/longwang155069/article/details/52935394

    前言
    根据上一节linux电源管理-概述可知,linux电源管理存在的几种方式,如何查看这几种方式,以及最后的如何睡眠唤醒等。
    通过echo mem > /sys/power/state就可以达到睡眠,所以可以根据此节点的sys代码分析suspend的流程。

    suspend代码分析
    在手机端执行如下命令:
    echo mem > /sys/power/state
    根据sys节点的属性命令规则,可以此节点的实现代码为:  state_store
    state_store函数分析
    static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
    const char *buf, size_t n)
    {
    suspend_state_t state;
    int error;

    error = pm_autosleep_lock();
    if (error)
    return error;

    if (pm_autosleep_state() > PM_SUSPEND_ON) {
    error = -EBUSY;
    goto out;
    }

    state = decode_state(buf, n);
    if (state < PM_SUSPEND_MAX)
    error = pm_suspend(state);
    else if (state == PM_SUSPEND_MAX)
    error = hibernate();
    else
    error = -EINVAL;

    out:
    pm_autosleep_unlock();
    return error ? error : n;
    }
    1) pm_autosleep_lock
    int pm_autosleep_lock(void)
    {
    return mutex_lock_interruptible(&autosleep_lock);
    }
    获得autosleep锁,锁住autosleep功能,此功能在后面分析。
    2. 判断当前autosleep的状态,如果当前状态大于PM_SUSPEND_ON则,返回退出。关于suspend的状态如下:
    #define PM_SUSPEND_ON ((__force suspend_state_t) 0)
    #define PM_SUSPEND_FREEZE ((__force suspend_state_t) 1)
    #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2)
    #define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
    #define PM_SUSPEND_MIN PM_SUSPEND_FREEZE
    #define PM_SUSPEND_MAX ((__force suspend_state_t) 4)
    3. 解析当前传入的state。如果state小于PM_SUSPEND_MAX就走suspend流程,等于PM_SUSPEND_MAX就走hibernate流程。加入我们传入的是mem, 则就会走suspend流程。
    pm_suspend函数分析
    int pm_suspend(suspend_state_t state)
    {
    int error;

    if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
    return -EINVAL;

    pm_suspend_marker("entry");
    error = enter_state(state);
    if (error) {
    suspend_stats.fail++;
    dpm_save_failed_errno(error);
    } else {
    suspend_stats.success++;
    }
    pm_suspend_marker("exit");
    return error;
    }
    1. 依然会再次判断当前的state是否在PM_SUSPEND_ON和PM_SUSPEND_MAX之间
    2. pm_suspend_marker("entry")
    static void pm_suspend_marker(char *annotation)
    {
    struct timespec ts;
    struct rtc_time tm;

    getnstimeofday(&ts);
    rtc_time_to_tm(ts.tv_sec, &tm);
    pr_info("PM: suspend %s %d-%02d-%02d %02d:%02d:%02d.%09lu UTC ",
    annotation, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
    tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
    }
    在suspend之间记录时间,用于统计或者调试suspend花费的时间
    3. 调用enter_state进入suspend的下一步,如果执行suspend成功,增加suspend.success的引用计数,否则增加suspend.fail的引用计数。
    enter_state函数分析
    static int enter_state(suspend_state_t state)
    {
    int error;

    trace_suspend_resume(TPS("suspend_enter"), state, true);
    if (state == PM_SUSPEND_FREEZE) {
    #ifdef CONFIG_PM_DEBUG
    if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
    pr_warning("PM: Unsupported test mode for freeze state,"
    "please choose none/freezer/devices/platform. ");
    return -EAGAIN;
    }
    #endif
    } else if (!valid_state(state)) {
    return -EINVAL;
    }
    if (!mutex_trylock(&pm_mutex))
    return -EBUSY;

    if (state == PM_SUSPEND_FREEZE)
    freeze_begin();

    trace_suspend_resume(TPS("sync_filesystems"), 0, true);
    printk(KERN_INFO "PM: Syncing filesystems ... ");
    sys_sync();
    printk("done. ");
    trace_suspend_resume(TPS("sync_filesystems"), 0, false);

    pr_debug("PM: Preparing system for %s sleep ", pm_states[state]);
    error = suspend_prepare(state);
    if (error)
    goto Unlock;

    if (suspend_test(TEST_FREEZER))
    goto Finish;

    trace_suspend_resume(TPS("suspend_enter"), state, false);
    pr_debug("PM: Entering %s sleep ", pm_states[state]);
    pm_restrict_gfp_mask();
    error = suspend_devices_and_enter(state);
    pm_restore_gfp_mask();

    Finish:
    pr_debug("PM: Finishing wakeup. ");
    suspend_finish();
    Unlock:
    mutex_unlock(&pm_mutex);
    return error;
    }
    1. 通过vaild_state函数用来判断该平台是否支持该状态睡眠。
    static bool valid_state(suspend_state_t state)
    {
    /*
    * PM_SUSPEND_STANDBY and PM_SUSPEND_MEM states need low level
    * support and need to be valid to the low level
    * implementation, no valid callback implies that none are valid.
    */
    return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
    }
    根据注释可知,standby和mem状态是处于低功耗状态下的,需要平台代码来支持实现的。因此内核使用platform_suspend_ops来定义各个平台的pm实现,然后通过suspend_set_ops函数设置具体平台pm到suspend_ops中。最终还是通过vaild函数来判断该平台是否支持需要睡眠的状态。
    内核也提供了只支持mem睡眠的函数
    int suspend_valid_only_mem(suspend_state_t state)
    {
    return state == PM_SUSPEND_MEM;
    }
    当然了如果state状态是freeze的话直接继续执行。
    2. 调用mutex_trylock获得一个mutex锁,防止在suspend的时候再次suspend。
    3. 如果当前state是PM_SUSPEND_FREEZE,则调用freeze_begin做开始准备工作。
    4. 同步文件系统。
    5. 调用suspend_prepare做进一步suspend前期准备工作,准备控制台,冻结内核线程等。
    6. 调用suspend_devices_and_enter做设备以及系统相关的susupend操作。
    7. 调用suspend_finish做最后的恢复工作。
    suspend_prepare函数分析
    /**
    * suspend_prepare - Prepare for entering system sleep state.
    *
    * Common code run for every system sleep state that can be entered (except for
    * hibernation). Run suspend notifiers, allocate the "suspend" console and
    * freeze processes.
    */
    static int suspend_prepare(suspend_state_t state)
    {
    int error;

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

    pm_prepare_console();

    error = pm_notifier_call_chain(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;
    }
    1. 检测该平台suspend_ops是否实现了enter函数
    static bool sleep_state_supported(suspend_state_t state)
    {
    return state == PM_SUSPEND_FREEZE || (suspend_ops && suspend_ops->enter);
    }
    2. 调用pm_prepare_console函数切换控制台,重新分配一个suspend模式下控制台,然后重定向kmsg。
    3. 通过调用pm通知链,发送PM_SUSPEND_PREPARE消息。
    int pm_notifier_call_chain(unsigned long val)
    {
    int ret = blocking_notifier_call_chain(&pm_chain_head, val, NULL);

    return notifier_to_errno(ret);
    }
    那谁会收到这类消息呢? 只有通过register_pm_notifier的设备,子系统会在这个时候处理自己的事情。
    int register_pm_notifier(struct notifier_block *nb)
    {
    return blocking_notifier_chain_register(&pm_chain_head, nb);
    }
    4. 调用suspend_freeze_processes冻结userhelper进程,已经内核线程。如果冻结出现失败,记录失败的引用计数。
    5. 接着会通过通知链恢复suspend,已经恢复控制台。

    suspend_devices_and_enter函数分析
    /**
    * suspend_devices_and_enter - Suspend devices and enter system sleep state.
    * @state: System sleep state to enter.
    */
    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();
    suspend_test_start();
    error = dpm_suspend_start(PMSG_SUSPEND);
    if (error) {
    pr_err("PM: Some devices failed to suspend, or early wake event detected ");
    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);
    } while (!error && !wakeup && platform_suspend_again(state));

    Resume_devices:
    suspend_test_start();
    dpm_resume_end(PMSG_RESUME);
    suspend_test_finish("resume devices");
    trace_suspend_resume(TPS("resume_console"), state, true);
    resume_console();
    trace_suspend_resume(TPS("resume_console"), state, false);

    Close:
    platform_resume_end(state);
    return error;

    Recover_platform:
    platform_recover(state);
    goto Resume_devices;
    }
    1. 调用sleep_state_supported函数判断当前平台是否实现了suspend_ops,已经suspend_ops->enter函数。
    2. 如果当前状态是freeze,就调用freeze_ops的begin函数。否则就调用平台相关的begin函数。这里的begin主要是各个平台pm的一些设置,每个平台的操作都不一样,这里不详细说明。
    static int platform_suspend_begin(suspend_state_t state)
    {
    if (state == PM_SUSPEND_FREEZE && freeze_ops && freeze_ops->begin)
    return freeze_ops->begin();
    else if (suspend_ops->begin)
    return suspend_ops->begin(state);
    else
    return 0;
    }
    3. 调用suspend_console挂起控制台,防止其它代码访问该控制台。
    4. 调用suspend_test_start记录当前suspend刚开始的时候的时间,使用jiffies表示。
    void suspend_test_start(void)
    {
    /* FIXME Use better timebase than "jiffies", ideally a clocksource.
    * What we want is a hardware counter that will work correctly even
    * during the irqs-are-off stages of the suspend/resume cycle...
    */
    suspend_test_start_time = jiffies;
    }
    5. 调用dpm_suspend_start函数,该函数主要是调用所有设备的prepare和suspend回调函数。如果出现suspend失败,则会打印"fail suspend"的log,以及调用platform_recover函数执行平台相关的recover回调。
    static void platform_recover(suspend_state_t state)
    {
    if (state != PM_SUSPEND_FREEZE && suspend_ops->recover)
    suspend_ops->recover();
    }
    6. 调用suspend_enter使整个系统进入suspend状态。
    dpm_suspend_start函数分析
    int dpm_suspend_start(pm_message_t state)
    {
    int error;

    error = dpm_prepare(state);
    if (error) {
    suspend_stats.failed_prepare++;
    dpm_save_failed_step(SUSPEND_PREPARE);
    } else
    error = dpm_suspend(state);
    return error;
    }
    1. 调用dpm_prepare函数,执行所有设备的prepare回调函数。执行顺序是pm_domain-type-class-bus-driver,如果失败设置failed_prepare的引用计数值。
    2. 调用dpm_suspend函数,执行所有设备的suspend回调函数。
    dpm_prepare函数分析
    /**
    * dpm_prepare - Prepare all non-sysdev devices for a system PM transition.
    * @state: PM transition of the system being carried out.
    *
    * Execute the ->prepare() callback(s) for all devices.
    */
    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)) {
    struct device *dev = to_device(dpm_list.next);

    get_device(dev);
    mutex_unlock(&dpm_list_mtx);

    error = device_prepare(dev, state);

    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 ",
    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);
    put_device(dev);
    }
    mutex_unlock(&dpm_list_mtx);
    trace_suspend_resume(TPS("dpm_prepare"), state.event, false);
    return error;
    }
    1.  判断dpm_list是否为空。那这个dpm_list是在哪里设置的呢? dpm_list是在device_add的时候调用device_pm_add函数,将当前的设备添加到dpm_list中的。
    void device_pm_add(struct device *dev)
    {
    pr_debug("PM: Adding info for %s:%s ",
    dev->bus ? dev->bus->name : "No Bus", dev_name(dev));
    mutex_lock(&dpm_list_mtx);
    if (dev->parent && dev->parent->power.is_prepared)
    dev_warn(dev, "parent %s should not be sleeping ",
    dev_name(dev->parent));
    list_add_tail(&dev->power.entry, &dpm_list);
    mutex_unlock(&dpm_list_mtx);
    }
    2. 调用get_device增加设备的引用计数,然后调用device_prepare函数调用设备的prepare回调。如果失败减少设备的引用计数。
    3. 设置该设备的is_prepared标志位,然后将该设备添加到dom_prepared_list链表中。
    device_prepare函数分析
    static int device_prepare(struct device *dev, pm_message_t state)
    {
    int (*callback)(struct device *) = NULL;
    char *info = NULL;
    int ret = 0;

    if (dev->power.syscore)
    return 0;

    /*
    * If a device's parent goes into runtime suspend at the wrong time,
    * it won't be possible to resume the device. To prevent this we
    * block runtime suspend here, during the prepare phase, and allow
    * it again during the complete phase.
    */
    pm_runtime_get_noresume(dev);

    device_lock(dev);

    dev->power.wakeup_path = device_may_wakeup(dev);

    if (dev->pm_domain) {
    info = "preparing power domain ";
    callback = dev->pm_domain->ops.prepare;
    } else if (dev->type && dev->type->pm) {
    info = "preparing type ";
    callback = dev->type->pm->prepare;
    } else if (dev->class && dev->class->pm) {
    info = "preparing class ";
    callback = dev->class->pm->prepare;
    } else if (dev->bus && dev->bus->pm) {
    info = "preparing bus ";
    callback = dev->bus->pm->prepare;
    }

    if (!callback && dev->driver && dev->driver->pm) {
    info = "preparing driver ";
    callback = dev->driver->pm->prepare;
    }

    if (callback) {
    trace_device_pm_callback_start(dev, info, state.event);
    ret = callback(dev);
    trace_device_pm_callback_end(dev, ret);
    }

    device_unlock(dev);

    if (ret < 0) {
    suspend_report_result(callback, ret);
    pm_runtime_put(dev);
    return ret;
    }
    /*
    * A positive return value from ->prepare() means "this device appears
    * to be runtime-suspended and its state is fine, so if it really is
    * runtime-suspended, you can leave it in that state provided that you
    * will do the same thing with all of its descendants". This only
    * applies to suspend transitions, however.
    */
    spin_lock_irq(&dev->power.lock);
    dev->power.direct_complete = ret > 0 && state.event == PM_EVENT_SUSPEND;
    spin_unlock_irq(&dev->power.lock);
    return 0;
    }
    此函数就是从设备的pm_domain, type, class,bus,driver一直调用下来。通常情况下就会调用到driver中的prepare函数中。
    dpm_suspend函数分析
    当对系统中的所有设备调用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)) {
    struct device *dev = to_device(dpm_prepared_list.prev);

    get_device(dev);
    mutex_unlock(&dpm_list_mtx);

    error = device_suspend(dev);

    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);
    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;
    }
     对之前加入dpm_prepared_list链表的设备,调用device_suspend函数。然后该此设备又加入到dpm_suspend_list链表中。如果出现suspend失败,就打印log,更新failed_suspend的值。在调用到device_suspend函数中,会判断是否支持异步suspend操作,这里不关心细节,主要分析主流程,最后调用到__device_suspend函数中。
    __device_suspend函数分析
    static int __device_suspend(struct device *dev, pm_message_t state, bool async)
    {
    pm_callback_t callback = NULL;
    char *info = NULL;
    int error = 0;
    struct timer_list timer;
    struct dpm_drv_wd_data data;
    char suspend_abort[MAX_SUSPEND_ABORT_LEN];
    DECLARE_DPM_WATCHDOG_ON_STACK(wd);

    dpm_wait_for_children(dev, async);

    if (async_error)
    goto Complete;

    /*
    * If a device configured to wake up the system from sleep states
    * has been suspended at run time and there's a resume request pending
    * for it, this is equivalent to the device signaling wakeup, so the
    * system suspend operation should be aborted.
    */
    if (pm_runtime_barrier(dev) && device_may_wakeup(dev))
    pm_wakeup_event(dev, 0);

    if (pm_wakeup_pending()) {
    pm_get_active_wakeup_sources(suspend_abort,
    MAX_SUSPEND_ABORT_LEN);
    log_suspend_abort_reason(suspend_abort);
    async_error = -EBUSY;
    goto Complete;
    }

    if (dev->power.syscore)
    goto Complete;

    data.dev = dev;
    data.tsk = get_current();
    init_timer_on_stack(&timer);
    timer.expires = jiffies + HZ * 12;
    timer.function = dpm_drv_timeout;
    timer.data = (unsigned long)&data;
    add_timer(&timer);

    if (dev->power.direct_complete) {
    if (pm_runtime_status_suspended(dev)) {
    pm_runtime_disable(dev);
    if (pm_runtime_suspended_if_enabled(dev))
    goto Complete;

    pm_runtime_enable(dev);
    }
    dev->power.direct_complete = false;
    }

    dpm_watchdog_set(&wd, dev);
    device_lock(dev);

    if (dev->pm_domain) {
    info = "power domain ";
    callback = pm_op(&dev->pm_domain->ops, state);
    goto Run;
    }

    if (dev->type && dev->type->pm) {
    info = "type ";
    callback = pm_op(dev->type->pm, state);
    goto Run;
    }

    if (dev->class) {
    if (dev->class->pm) {
    info = "class ";
    callback = pm_op(dev->class->pm, state);
    goto Run;
    } else if (dev->class->suspend) {
    pm_dev_dbg(dev, state, "legacy class ");
    error = legacy_suspend(dev, state, dev->class->suspend,
    "legacy class ");
    goto End;
    }
    }

    if (dev->bus) {
    if (dev->bus->pm) {
    info = "bus ";
    callback = pm_op(dev->bus->pm, state);
    } else if (dev->bus->suspend) {
    pm_dev_dbg(dev, state, "legacy bus ");
    error = legacy_suspend(dev, state, dev->bus->suspend,
    "legacy bus ");
    goto End;
    }
    }

    Run:
    if (!callback && dev->driver && dev->driver->pm) {
    info = "driver ";
    callback = pm_op(dev->driver->pm, state);
    }

    error = dpm_run_callback(callback, dev, state, info);

    End:
    if (!error) {
    struct device *parent = dev->parent;

    dev->power.is_suspended = true;
    if (parent) {
    spin_lock_irq(&parent->power.lock);

    dev->parent->power.direct_complete = false;
    if (dev->power.wakeup_path
    && !dev->parent->power.ignore_children)
    dev->parent->power.wakeup_path = true;

    spin_unlock_irq(&parent->power.lock);
    }
    }

    device_unlock(dev);
    dpm_watchdog_clear(&wd);

    del_timer_sync(&timer);
    destroy_timer_on_stack(&timer);

    Complete:
    complete_all(&dev->power.completion);
    if (error)
    async_error = error;

    return error;
    }
    1.  调用dpm_wait_for_children使用异步等待该设备的所有孩子就绪。
    2.  如果此时有wakup事件发生,应该停止系统suspend。
    3.  如果没有wakup事件发生,创建一个12s的定时器,然后启动定时器。如果在12s之内suspend没有处理完成,就打印call stack,导致系统panic。
    static void dpm_drv_timeout(unsigned long data)
    {
    struct dpm_drv_wd_data *wd_data = (void *)data;
    struct device *dev = wd_data->dev;
    struct task_struct *tsk = wd_data->tsk;

    printk(KERN_EMERG "**** DPM device timeout: %s (%s) ", dev_name(dev),
    (dev->driver ? dev->driver->name : "no driver"));

    printk(KERN_EMERG "dpm suspend stack: ");
    show_stack(tsk, NULL);

    BUG();
    }
    4. 判断该设备是否在suspend之前已经发生了runtime_suspend。如果该设备已经处于suspend则可以直接返回。
    5. 依次调用subsystem-level(pm_domain, type, class, bus)级别的suspend回调函数,如果subsystem-level级别的suspend回调函数都没有实现,则调用driver的suspend回调。
    6. 销毁之前创建的定时器。
    suspend_enter函数分析
    在之前对dpm_suspend_start函数进行了分析,该函数中主要是调用所有设备的prepare和suspend回调函数。而在suspend_enter主要是使系统进入到suspend中。
    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);
    if (error)
    goto Platform_finish;

    error = dpm_suspend_late(PMSG_SUSPEND);
    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 ");
    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);
    if (error)
    goto Devices_early_resume;

    error = dpm_suspend_noirq(PMSG_SUSPEND);
    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 ");
    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) {
    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();
    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);

    Platform_early_resume:
    platform_resume_early(state);

    Devices_early_resume:
    dpm_resume_early(PMSG_RESUME);

    Platform_finish:
    platform_resume_finish(state);
    return error;
    }
    1.  调用平台相关prepare回调函数,如果平台prepare设置失败,在调用平台相关的finish回调函数。
    2.  调用dpm_suspend_late函数。此函数主要调用dpm_suspend_list中的设备的suspend_late回调函数,然后又将这些设备加入到dpm_late_early_list链表中。如果出现失败,则跳到platform_finish做恢复工作。
    3.  如果当前休眠状态是PM_SUSPEND_FREEZE的话,调用freeze_ops中的prepare回调。
    4.  调用dpm_suspend_noirq函数,此函数主要是从dpm_late_early_list链表中取一个设备,然后调用该设备的suspend_noirq回调,同时将该设备加入到dpm_noirq_list链表中。
    5.  回调平台相关的preplate_late函数,做suspend最后关头的事情。
    6.  如果休眠状态是PM_SUSPEND_FREEZE,则frozen processes + suspended devices + idle processors
    7.  disable所有非nonboot的CPU,失败之后启动CPU。
    8.  关掉全局cpu中断,如果关掉中断,则报BUG
    arch_suspend_disable_irqs();
    BUG_ON(!irqs_disabled());
    9.  执行所有system core的suspend回调函数。
    10.  如果执行成功的话,这时候系统还会调用pm_wakeup_pending检查下,是否有唤醒事件发生,如果发生停止suspend,恢复系统。
    11.  这时候系统已经睡眠,如果这时候有唤醒事件发生,比如按下手机的power按键,系统又会接着suspend的地方,再次往下执行。也就是suspend的一些列反操作。

    suspend/resume过程总结
    如下是suspend/resume过程的简图


    以上就是整个系统的suspend/resume执行过程,但是对于一般的驱动开发工程师来说主要关心的是Device Suspend和Device Resume过程。
    suspend:  prepare->suspend->suspend_late->suspend_noirq
    resume: resume_noirq->resume_early->resume->complete
    ————————————————
    版权声明:本文为CSDN博主「Loopers」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/longwang155069/article/details/52935394

  • 相关阅读:
    海量文件查重SimHash和Minhash
    刷题中熟悉Shell命令之Tenth Line和Transpose File [leetcode]
    C# 开发XML Web Service与Java开发WebService
    Sketchup+ArcGIS三维建模与管理
    入门-Arcmap网络分析示例
    建议入门-用ArcMap进行空间查询与空间连接
    (转载)Htmlparser Filter 简要归纳
    (转载)java常见的ClassNotFoundException
    C# WinForm程序向datagridview里添加数据
    oracle数据库的简单操作
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/12524761.html
Copyright © 2011-2022 走看看