Linux电源管理-SuspendResume流程
前⾔
根据上⼀节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,
<_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)
return0;
suspend_stats.failed_freeze++;
linux下的sleep函数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函数分析
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论