`
womendu
  • 浏览: 1481377 次
  • 性别: Icon_minigender_2
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

标准linu休眠和唤醒机制分析(三)

阅读更多

五、suspendresume代码走读

下面对suspend分的几个阶段都是按照pm test5中模式来划分的:freezerdevicesplatformprocessorscore

suspend第一阶段:freezer

int enter_state(suspend_state_t state)

{

int error;

if (!valid_state(state))

return -ENODEV;

if (!mutex_trylock(&pm_mutex)) // def in kernel/kernel/power/main.c

return -EBUSY;

printk(KERN_INFO "PM: Syncing filesystems ... ");

sys_sync();

printk("done.\n"); // 同步文件系统

pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);

error = suspend_prepare();

// suspend前准备工作:Run suspend notifiers, allocate a console and stop all processes

if (error) // 如果一些准备工作失败,通常为冻结进程的时候某些进程拒绝进入冻结模式

goto Unlock; // 释放锁,然后退出

if (suspend_test(TEST_FREEZER))

// 检查上层下达的命令是否是:

// echo freezer > /sys/power/pm_test

// echo mem > /sys/power/state

// 是的话,延时5s后,然后做一些解冻进程等工作就返回

goto Finish;

pr_debug("PM: Entering %s sleep\n", pm_states[state]);

error = suspend_devices_and_enter(state); // 休眠外设

Finish:

pr_debug("PM: Finishing wakeup.\n");

suspend_finish(); // 解冻进程,发广播通知等

Unlock:

mutex_unlock(&pm_mutex);

return error;

}

static int suspend_prepare(void)

{

int error;

if (!suspend_ops || !suspend_ops->enter) // mtk_pm_enter() in mtkpm.c

return -EPERM;

pm_prepare_console(); //suspend分配一个虚拟终端来输出信息

error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);

// 广播一个通知给通知链pm_chain_head,该通知链上都是用函数register_pm_notifier()注册的pm通知项(也可以用宏pm_notifier定义和注册),这里按照注册时候的定义的优先级来调用该通知链上注册的回调函数。

// PM_SUSPEND_PREPARE是事件值,表示将要进入suspend

if (error)

goto Finish;

error = usermodehelper_disable(); // 关闭用户态的helper进程

if (error)

goto Finish;

error = suspend_freeze_processes();

// 冻结所有的进程, 这里会保存所有进程当前的状态。

if (!error)

return 0;

// 也许有一些进程会拒绝进入冻结状态, 当有这样的进程存在的时候, 会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程.

suspend_thaw_processes(); // 进程解冻

usermodehelper_enable(); // enable helper process

Finish:

pm_notifier_call_chain(PM_POST_SUSPEND); // 广播退出suspend的通知

pm_restore_console();

return error;

}

// 如果不支持pm debug的话,该函数直接返回0

static int suspend_test(int level)

{

#ifdef CONFIG_PM_DEBUG

if (pm_test_level == level) {

printk(KERN_INFO "suspend debug: Waiting for 5 seconds.\n");

mdelay(5000);

return 1;

}

#endif /* !CONFIG_PM_DEBUG */

return 0;

}

static void suspend_finish(void)

{

suspend_thaw_processes();

usermodehelper_enable();

pm_notifier_call_chain(PM_POST_SUSPEND);

pm_restore_console();

}

同步文件系统函数sys_sync()调用后,将会执行函数suspend_prepare()来做一些进入suspend之前的准备工作:Run suspend notifiers, allocate a console and stop all processesdisable user mode hleper process。之后将会调用suspend_test(TEST_FREEZER)来判断上层是否有下这样的命令下来:echo freezer > /sys/power/pm_test(如果pm debug支持),如果没有下这个命令或者系统根本就不支持pm debug,那么直接返回0。如果该测试函数返回了1,那么就不会继续往下走suspend的流程了,而是调用函数suspend_finish()来结束freezer模式的pm test。返回0,将会进入第二阶段继续suspend的流程。

suspend第二阶段:devices

后续所有对suspend划分的阶段都包含在函数suspend_devices_and_enter(state)之中,所以这个函数是关键所在。

int suspend_devices_and_enter(suspend_state_t state)

{

int error;

if (!suspend_ops)

// 一个重要的函数指针结构体,特定平台的不一样,

// kernel/arch/arm/mach-mt6516/mtkpm.c

return -ENOSYS;

if (suspend_ops->begin) {

error = suspend_ops->begin(state);

// 调用特定平台实现的suspend_begin函数,

// 这里没做什么实际的工作,打印了点字符串

if (error)

goto Close; // 如果有错,执行特定平台的suspend_end函数

}

suspend_console(); // suspend console subsystem

suspend_test_start(); // suspend devices超时警告测试

error = dpm_suspend_start(PMSG_SUSPEND); // suspend all devices, 外设休眠

// 关键函数之一, kernel/drivers/base/power/main.c

if (error) {

printk(KERN_ERR "PM: Some devices failed to suspend\n");

goto Recover_platform; // 如果外设休眠过程出现错误,将会终止suspend过程,直接跳到某标签出resume外设

}

suspend_test_finish("suspend devices");

if (suspend_test(TEST_DEVICES)) // suspend第二阶段以此为界

goto Recover_platform;

suspend_enter(state); // 关键函数之一, pm test的后三种模式都在该函数中进行测试:platformcpuscore

Resume_devices:

suspend_test_start();

dpm_resume_end(PMSG_RESUME);

suspend_test_finish("resume devices");

resume_console();

Close:

if (suspend_ops->end)

suspend_ops->end();

return error;

Recover_platform:

if (suspend_ops->recover)

suspend_ops->recover(); // 该函数目前的平台没有实现

goto Resume_devices;

}

@kernel/drivers/base/power/main.c

int dpm_suspend_start(pm_message_t state)

{

int error;

might_sleep();

error = dpm_prepare(state);

if (!error)

error = dpm_suspend(state); // 这两个函数的架构有一些类似

return error;

这两个函数如果在执行某一个device->prepare()->suspend回调函数的时候出错,都将会直接跳出返回错误码,不理会后续devices

}

static int dpm_prepare(pm_message_t state)

{

struct list_head list;

int error = 0;

INIT_LIST_HEAD(&list);

mutex_lock(&dpm_list_mtx); // 锁住dpm_list链表

transition_started = true; // device_pm_add()中使用

while (!list_empty(&dpm_list)) { // dpm_list上挂着所有的devices

// 关于device中的这部分内容,参考文档:

// 新版linux系统设备架构中关于电源管理方式的变更.txt

struct device *dev = to_device(dpm_list.next); // 取得对应的device结构体

get_device(dev);

dev->power.status = DPM_PREPARING;

// 将该设备的电源状态设置成DPM_PREPARING,表示该设备正准备着进入相应的省电模式,这之后就会调用和该设备有关的所以的-->prepare函数

mutex_unlock(&dpm_list_mtx);

pm_runtime_get_noresume(dev);

// 电源管理新方式中比较复杂的用法,这里没有使用到,所以直接跳过

if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {

/* Wake-up requested during system sleep transition. */

pm_runtime_put_noidle(dev);

error = -EBUSY;

} else {

error = device_prepare(dev, state);

// 这个才是真正执行-->prepare回调函数的地方

}

mutex_lock(&dpm_list_mtx);

if (error) {

dev->power.status = DPM_ON;

if (error == -EAGAIN) { // try again

put_device(dev);

error = 0;

continue;

}

printk(KERN_ERR "PM: Failed to prepare device %s "

"for power transition: error %d\n",

kobject_name(&dev->kobj), error);

put_device(dev);

break;

}

dev->power.status = DPM_SUSPENDING;

// 这之后就不能以它为父设备再注册设备了,可以从函数device_pm_add

// 中看出来

if (!list_empty(&dev->power.entry))

list_move_tail(&dev->power.entry, &list);

// 为了该函数中循环链表之用, list_empty(&dpm_list)

put_device(dev);

}

list_splice(&list, &dpm_list);

mutex_unlock(&dpm_list_mtx);

return error;

}

从这个函数我们可以发现,每一个devicedev->power.status状态都会有如下轨迹的变化:DPM_ON(device刚刚注册完的初始状态) --> DPM_PREPARING --> DPM_SUSPENDING --> 未完待续...

static int device_prepare(struct device *dev, pm_message_t state)

{

int error = 0;

down(&dev->sem);

// dev->bus绝大多数都存在,dev->bus->pm这个就不一定了,不过platform bus一定存在,而i2c bus就没有,dev->bus->pm->prepare这个函数如果存在就会被后面给调用到,以platform bus为例,该函数的实现完全回去调用device对应的driver->pm->prepare()函数(如果存在的话),当然driver->pm->prepare()不存在就什么也不做了

if (dev->bus && dev->bus->pm && dev->bus->pm->prepare) {

pm_dev_dbg(dev, state, "preparing ");

error = dev->bus->pm->prepare(dev);

suspend_report_result(dev->bus->pm->prepare, error);

if (error)

goto End;

}

// dev->type这个很多设备都有,但是dev->type->pm这个却很少有了

if (dev->type && dev->type->pm && dev->type->pm->prepare) {

pm_dev_dbg(dev, state, "preparing type ");

error = dev->type->pm->prepare(dev);

suspend_report_result(dev->type->pm->prepare, error);

if (error)

goto End;

}

// dev->class很少有设备有

if (dev->class && dev->class->pm && dev->class->pm->prepare) {

pm_dev_dbg(dev, state, "preparing class ");

error = dev->class->pm

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics