androidlisttransitionfilesystemsstructsystem
前面我们分析了休眠的第一个阶段即浅度休眠,现在我们继续看休眠的第二个阶段 — 深度休眠。在深度休眠的过程中系统会首先冻结所有可以冻结的进程,然后依次挂起所有设备的电源,挂起顺序与设备注册的顺序相反,这样保证了设备之间电源的依赖性;直至最后进入省电模式,等待用户或者RTC唤醒;在唤醒过程中则会按照设备注册的顺序依次恢复每个设备的电源进入正常工作状态,解冻相关的进程,然后再进行浅度休眠的唤醒流程。
1、深度休眠入口
根据wake_lock一节的分析我们知道driver层进入深度休眠的入口有4个,分别为expire_timer、wake_lock、wake_lock_timeout、wake_unlock,这几个入口函数将根据相应的条件启动suspend_work里面的pm_suspend()函数进入深度休眠流程,代码在linux/kernel/power/suspend.c中:
- // 进入深度休眠流程
- int enter_state(suspend_state_t state)
- {
- int error;
- // 判断平台是否支持该状态
- if (!valid_state(state))
- return -ENODEV;
- if (!mutex_trylock(&pm_mutex))
- 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();
- if (error)
- goto Unlock;
- // suspend_test
- if (suspend_test(TEST_FREEZER))
- 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;
- }
- int pm_suspend(suspend_state_t state)
- {
- if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
- return enter_state(state);
- return -EINVAL;
- }
- EXPORT_SYMBOL(pm_suspend);
在enter_state()中首先进入状态的判断,根据平台的特性判断是否支持此状态;然后再同步缓存;接着调用suspend_prepare()冻结大部分进程;然后再通过suspend_devices_and_enter()开始挂起设备。
2、冻结进程
- static int suspend_prepare(void)
- {
- int error;
- if (!suspend_ops || !suspend_ops->enter)
- return -EPERM;
- pm_prepare_console();
- // 通知进行休眠准备
- error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
- if (error)
- goto Finish;
- // 禁止usermodehelper
- error = usermodehelper_disable();
- if (error)
- goto Finish;
- // 冻结所有可以冻结的进程
- error = suspend_freeze_processes();
- if (!error)
- return 0;
- // 解冻所有进程
- suspend_thaw_processes();
- // 使能usermodehelper
- usermodehelper_enable();
- Finish:
- // 通知休眠结束
- pm_notifier_call_chain(PM_POST_SUSPEND);
- pm_restore_console();
- return error;
- }
这里有一个notifier机制后面要专门分析下。
3、挂起设备
- int suspend_devices_and_enter(suspend_state_t state)
- {
- int error;
- if (!suspend_ops)
- return -ENOSYS;
- // 处理器的休眠开始函数
- if (suspend_ops->begin) {
- error = suspend_ops->begin(state);
- if (error)
- goto Close;
- }
- // 休眠串口
- suspend_console();
- suspend_test_start();
- // 设备休眠
- error = dpm_suspend_start(PMSG_SUSPEND);
- if (error) {
- printk(KERN_ERR "PM: Some devices failed to suspend\n");
- goto Recover_platform;
- }
- suspend_test_finish("suspend devices");
- if (suspend_test(TEST_DEVICES))
- goto Recover_platform;
- // 处理器休眠
- suspend_enter(state);
- 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;
- }
- int dpm_suspend_start(pm_message_t state)
- {
- int error;
- might_sleep();
- error = dpm_prepare(state);
- if (!error)
- error = dpm_suspend(state);
- return error;
- }
- EXPORT_SYMBOL_GPL(dpm_suspend_start);
- // 函数将会调用所有的非sysdev设备的prepare()接口
- static int dpm_prepare(pm_message_t state)
- {
- struct list_head list;
- int error = 0;
- INIT_LIST_HEAD(&list);
- mutex_lock(&dpm_list_mtx);
- transition_started = true;
- // 遍历设备链表
- while (!list_empty(&dpm_list)) {
- // 从最先初始化的节点开始遍历
- struct device *dev = to_device(dpm_list.next);
- // 获取设备
- get_device(dev);
- // 更新设备状态
- dev->power.status = DPM_PREPARING;
- 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 { // 执行prepare()函数
- error = device_prepare(dev, state);
- }
- mutex_lock(&dpm_list_mtx);
- // 如果出错则跳出循环
- if (error) {
- dev->power.status = DPM_ON;
- if (error == -EAGAIN) {
- 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;
- if (!list_empty(&dev->power.entry))
- // 将设备节点移动到list链表中
- list_move_tail(&dev->power.entry, &list);
- put_device(dev);
- }
- // 拼接链表
- list_splice(&list, &dpm_list);
- mutex_unlock(&dpm_list_mtx);
- return error;
- }
- static int dpm_suspend(pm_message_t state)
- {
- struct list_head list;
- int error = 0;
- INIT_LIST_HEAD(&list);
- mutex_lock(&dpm_list_mtx);
- while (!list_empty(&dpm_list)) {
- // 逆序遍历链表,即先suspend后注册的设备,符合设备与父设备电源挂起的先后原则
- struct device *dev = to_device(dpm_list.prev);
- get_device(dev);
- mutex_unlock(&dpm_list_mtx);
- dpm_drv_wdset(dev);
- error = device_suspend(dev, state);
- dpm_drv_wdclr(dev);
- mutex_lock(&dpm_list_mtx);
- if (error) {
- pm_dev_err(dev, state, "", error);
- put_device(dev);
- break;
- }
- dev->power.status = DPM_OFF;
- if (!list_empty(&dev->power.entry))
- list_move(&dev->power.entry, &list);
- put_device(dev);
- }
- list_splice(&list, dpm_list.prev);
- mutex_unlock(&dpm_list_mtx);
- return error;
- }
函数将设备按照注册顺序反向挂起,挂起执行的流程如下:
- static int device_suspend(struct device *dev, pm_message_t state)
- {
- int error = 0;
- down(&dev->sem);
- if (dev->class) { // 类的suspend优先
- if (dev->class->pm) {
- pm_dev_dbg(dev, state, "class ");
- error = pm_op(dev, dev->class->pm, state);
- } else if (dev->class->suspend) {
- pm_dev_dbg(dev, state, "legacy class ");
- error = dev->class->suspend(dev, state);
- suspend_report_result(dev->class->suspend, error);
- }
- if (error)
- goto End;
- }
- if (dev->type) { // device_type次之
- if (dev->type->pm) {
- pm_dev_dbg(dev, state, "type ");
- error = pm_op(dev, dev->type->pm, state);
- }
- if (error)
- goto End;
- }
- if (dev->bus) { // bus优先级最低
- if (dev->bus->pm) {
- pm_dev_dbg(dev, state, "");
- error = pm_op(dev, dev->bus->pm, state);
- } else if (dev->bus->suspend) {
- pm_dev_dbg(dev, state, "legacy ");
- error = dev->bus->suspend(dev, state);
- suspend_report_result(dev->bus->suspend, error);
- }
- }
- End:
- up(&dev->sem);
- return error;
- }
4、挂起处理器
- static int suspend_enter(suspend_state_t state)
- {
- int error;
- // 处理器的休眠准备函数
- if (suspend_ops->prepare) {
- error = suspend_ops->prepare();
- if (error)
- return error;
- }
- // 执行非sysdev的late suspend函数
- error = dpm_suspend_noirq(PMSG_SUSPEND);
- if (error) {
- printk(KERN_ERR "PM: Some devices failed to power down\n");
- goto Platfrom_finish;
- }
- // 处理器休眠最后的准备
- if (suspend_ops->prepare_late) {
- error = suspend_ops->prepare_late();
- if (error)
- goto Power_up_devices;
- }
- if (suspend_test(TEST_PLATFORM))
- goto Platform_wake;
- // 关闭非启动cpu
- error = disable_nonboot_cpus();
- if (error || suspend_test(TEST_CPUS))
- goto Enable_cpus;
- // 挂起中断
- arch_suspend_disable_irqs();
- BUG_ON(!irqs_disabled());
- // 挂起sysdev
- error = sysdev_suspend(PMSG_SUSPEND);
- if (!error) {
- if (!suspend_test(TEST_CORE))
- // 处理器的休眠进入函数,休眠流程运行至此
- error = suspend_ops->enter(state);
- // 唤醒sysdev
- sysdev_resume();
- }
- // 使能中断
- arch_suspend_enable_irqs();
- BUG_ON(irqs_disabled());
- Enable_cpus:
- // 使能非启动cpu
- enable_nonboot_cpus();
- Platform_wake:
- // 处理器开始唤醒
- if (suspend_ops->wake)
- suspend_ops->wake();
- Power_up_devices:
- // 执行非sysdev的early resume函数
- dpm_resume_noirq(PMSG_RESUME);
- Platfrom_finish:
- // 处理器休眠结束
- if (suspend_ops->finish)
- suspend_ops->finish();
- return error;
- }
在这个阶段首先看处理器是否需要做一些准备,接下来执行非sysdev的late suspend函数,然后处理器做休眠前最后的准备、关闭非启动cpu、挂起中断,再挂起sysdev,最后进入处理器的挂起函数,至此休眠流程结束,处理器等待用户或者RTC唤醒。
附1、late suspend
在这里我们看到了一种新的suspend机制 — late suspend,是在所有的suspend执行完后再开始执行,接口为dev->bus->pm->suspend_noirq;这样early_suspend、suspend以及late suspend构成了suspend的三部曲,late suspend是在中断关闭的情况下进行的;前面我们分析的wake_lock就有用到,用于检测在suspend阶段是否有锁被激活。late suspend的实现如下:
- int dpm_suspend_noirq(pm_message_t state)
- {
- struct device *dev;
- int error = 0;
- suspend_device_irqs(); // 关闭除唤醒系统以外的所有中断
- mutex_lock(&dpm_list_mtx);
- list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
- // 执行所有设备的late suspend函数
- error = device_suspend_noirq(dev, state);
- if (error) {
- pm_dev_err(dev, state, " late", error);
- break;
- }
- dev->power.status = DPM_OFF_IRQ;
- }
- mutex_unlock(&dpm_list_mtx);
- if (error)
- dpm_resume_noirq(resume_event(state));
- return error;
- }
- EXPORT_SYMBOL_GPL(dpm_suspend_noirq);
附2、中断关闭流程
在late suspend机制中我们看到了休眠流程中关闭系统中断的地方:- void suspend_device_irqs(void)
- {
- struct irq_desc *desc;
- int irq;
- for_each_irq_desc(irq, desc) { // 遍历系统的中断
- unsigned long flags;
- spin_lock_irqsave(&desc->lock, flags);
- __disable_irq(desc, irq, true); // 关闭中断
- spin_unlock_irqrestore(&desc->lock, flags);
- }
- for_each_irq_desc(irq, desc)
- if (desc->status & IRQ_SUSPENDED)
- synchronize_irq(irq);
- }
- EXPORT_SYMBOL_GPL(suspend_device_irqs);
- void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
- {
- if (suspend) {
- // 如果中断没有被激活或者中断的IRQF_TIMER标志被置位则不关闭中断
- // 在以后的内核版本中这个标志位被换成了IRQF_NO_SUSPEND
- // 新版的IRQF_TIMER = (__IRQF_TIMER | IRQF_NO_SUSPEND)
- if (!desc->action || (desc->action->flags & IRQF_TIMER))
- return;
- desc->status |= IRQ_SUSPENDED;
- }
- // 判断中断是否被打开
- if (!desc->depth++) {
- // 更新标志位
- desc->status |= IRQ_DISABLED;
- // 关闭中断
- desc->chip->disable(irq);
- }
- }
附3、dpm_list链表
dpm_list是内核中用于设备电源管理的链表,设备注册时通过一系列的调用 device_register() -> device_add() -> device_pm_add() 最后在device_pm_add()中将设备加入dpm_list链表中:
- // 设备创建时都会调用的函数,将设备加入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",
- kobject_name(&dev->kobj));
- mutex_lock(&dpm_list_mtx);
- if (dev->parent) {
- if (dev->parent->power.status >= DPM_SUSPENDING)
- dev_warn(dev, "parent %s should not be sleeping\n",
- dev_name(dev->parent));
- } else if (transition_started) {
- /*
- * We refuse to register parentless devices while a PM
- * transition is in progress in order to avoid leaving them
- * unhandled down the road
- */
- dev_WARN(dev, "Parentless device registered during a PM transaction\n");
- }
- // 将设备节点添加到链表尾部,即设备按注册的先后顺序从链表头部到尾部
- list_add_tail(&dev->power.entry, &dpm_list);
- mutex_unlock(&dpm_list_mtx);
- }
- // 设备注销时都会调用的函数,将设备从dpm_list链表中移除
- void device_pm_remove(struct device *dev)
- {
- pr_debug("PM: Removing info for %s:%s\n",
- dev->bus ? dev->bus->name : "No Bus",
- kobject_name(&dev->kobj));
- mutex_lock(&dpm_list_mtx);
- list_del_init(&dev->power.entry);
- mutex_unlock(&dpm_list_mtx);
- pm_runtime_remove(dev);
- }