1. 程式人生 > >Linux電源管理-Suspend/Resume流程

Linux電源管理-Suspend/Resume流程

前言

根據上一節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\n",  
        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.\n");  
            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.\n");  
    trace_suspend_resume(TPS("sync_filesystems"), 0, false);  

    pr_debug("PM: Preparing system for %s sleep\n", 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\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();  
 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\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);  
    } 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\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);  
        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\n",  
         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\n",  
            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)\n", dev_name(dev),  
           (dev->driver ? dev->driver->name : "no driver"));  

    printk(KERN_EMERG "dpm suspend stack:\n");  
    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\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);  
    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\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) {  
        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