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\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
————————————————
版權聲明:本文為CSDN博主「Loopers」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/longwang155069/article/details/52935394


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM