suspend-resume(3)
来源:互联网 发布:听力复读软件 编辑:程序博客网 时间:2024/06/05 07:19
跟休眠唤醒相关的文件:
- linux_source/kernel/power/main.c
- linux_source/kernel/power/earlysuspend.c
- linux_source/kernel/power/wakelock.c
- linux_source/kernel/power/suspend.c
- linux_source/kernel/power/power.h
- linux_source/kernel/power/process.c
- linux_source/drivers/base/power/main.c
- linux_source/arch/arm/plat-samsung/pm.c
- android\hardware\libhardware_legacy\power\power.c
- android\frameworks\base\core\jni\android_os_Power.cpp
- android\frameworks\base\core\java\android\os\Power.java
- android\frameworks\base\core\java\android\os\PowerManager.java
- android\frameworks\base\services\java\com\android\server\PowerManagerService.java
- android\frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java
Android 休眠过程
当用户读写/sys/power/state时,linux_source/kernel/power/main.c中的state_store()函数会被调用。其中,android的early_suspend会执行request_suspend_state(state); 而标准的linux休眠则执行error = enter_state(state)
- linux_source/kernel/power/main.c
- static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
- const char *buf, size_t n)
- {
- #ifdef CONFIG_SUSPEND
- #ifdef CONFIG_EARLYSUSPEND
- suspend_state_t state = PM_SUSPEND_ON;
- #else
- suspend_state_t state = PM_SUSPEND_STANDBY;
- #endif
- const char * const *s;
- #endif
- char *p;
- int len;
- int error = -EINVAL;
- p = memchr(buf, '\n', n);
- len = p ? p - buf : n;
- /* First, check if we are requested to hibernate */
- if (len == 4 && !strncmp(buf, "disk", len)) {
- error = hibernate();
- goto Exit;
- }
- #ifdef CONFIG_SUSPEND
- for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
- if (*s && len == strlen(*s) && !strncmp(buf, *s, len))
- break;
- }
- if (state < PM_SUSPEND_MAX && *s)
- #ifdef <strong><span style="color:#3366ff;">CONFIG_EARLYSUSPEND</span></strong>
- if (state == PM_SUSPEND_ON || valid_state(state)) {
- error = 0;
- <span style="color:#3366ff;"><strong>request_suspend_state(state);</strong></span>
- }
- #else
- error = enter_state(state);
- #endif
- #endif
- Exit:
- return error ? error : n;
- }
在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中
- linux_source/kernel/power/earlysuspend.c
- static DECLARE_WORK(early_suspend_work, early_suspend);
- void request_suspend_state(suspend_state_t new_state)
- {
- unsigned long irqflags;
- int old_sleep;
- spin_lock_irqsave(&state_lock, irqflags);
- old_sleep = state & SUSPEND_REQUESTED;
- if (debug_mask & DEBUG_USER_STATE) {
- struct timespec ts;
- struct rtc_time tm;
- getnstimeofday(&ts);
- rtc_time_to_tm(ts.tv_sec, &tm);
- pr_info("request_suspend_state: %s (%d->%d) at %lld "
- "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
- new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
- requested_suspend_state, new_state,
- ktime_to_ns(ktime_get()),
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
- }
- if (!old_sleep && new_state != PM_SUSPEND_ON) {
- state |= SUSPEND_REQUESTED;
- <span style="color:#3366ff;"><strong>queue_work(suspend_work_queue, &early_suspend_work);</strong></span>
- } else if (old_sleep && new_state == PM_SUSPEND_ON) {
- state &= ~SUSPEND_REQUESTED;
- wake_lock(&main_wake_lock);
- <span style="color:#3366ff;"><strong>queue_work(suspend_work_queue, &late_resume_work);</strong></span>
- }
- requested_suspend_state = new_state;
- spin_unlock_irqrestore(&state_lock, irqflags);
- }
在early_suspend()函数中,首先要判断当前请求的状态是否还是suspend,若不是,则直接退出了;若是,函数会调用已经注册的early_suspend的函数,然后同步文件系统,最后释放main_wake_lock。
- linux_source/kernel/power/earlysuspend.c
- static void early_suspend(struct work_struct *work)
- {
- struct early_suspend *pos;
- unsigned long irqflags;
- int abort = 0;
- mutex_lock(&early_suspend_lock);
- spin_lock_irqsave(&state_lock, irqflags);
- if (state == SUSPEND_REQUESTED)
- state |= SUSPENDED;
- else
- abort = 1;
- spin_unlock_irqrestore(&state_lock, irqflags);
- if (abort) {
- if (debug_mask & DEBUG_SUSPEND)
- pr_info("early_suspend: abort, state %d\n", state);
- mutex_unlock(&early_suspend_lock);
- goto abort;
- }
- if (debug_mask & DEBUG_SUSPEND)
- pr_info("early_suspend: call handlers\n");
- list_for_each_entry(pos, &early_suspend_handlers, link) {
- if (pos->suspend != NULL) {
- if (debug_mask & DEBUG_VERBOSE)
- pr_info("early_suspend: calling %pf\n", pos->suspend);
- pos->suspend(pos);
- }
- }
- mutex_unlock(&early_suspend_lock);
- if (debug_mask & DEBUG_SUSPEND)
- pr_info("early_suspend: sync\n");
- sys_sync();
- abort:
- spin_lock_irqsave(&state_lock, irqflags);
- if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
- <span style="color:#3366ff;"><strong>wake_unlock(&main_wake_lock);</strong></span>
- spin_unlock_irqrestore(&state_lock, irqflags);
- }
在wake_unlock()中,删除链表中wake_lock节点,判断当前是否存在wake_lock,若wake_lock的数目为0,则调用工作队列suspend_work,进入suspend状态。
- linux_source/kernel/power/wakelock.c
- static DECLARE_WORK(suspend_work, suspend);
- void wake_unlock(struct wake_lock *lock)
- {
- int type;
- unsigned long irqflags;
- spin_lock_irqsave(&list_lock, irqflags);
- type = lock->flags & WAKE_LOCK_TYPE_MASK;
- #ifdef CONFIG_WAKELOCK_STAT
- wake_unlock_stat_locked(lock, 0);
- #endif
- if (debug_mask & DEBUG_WAKE_LOCK)
- pr_info("wake_unlock: %s\n", lock->name);
- lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
- list_del(&lock->link);
- list_add(&lock->link, &inactive_locks);
- if (type == WAKE_LOCK_SUSPEND) {
- long has_lock = has_wake_lock_locked(type);
- if (has_lock > 0) {
- if (debug_mask & DEBUG_EXPIRE)
- pr_info("wake_unlock: %s, start expire timer, "
- "%ld\n", lock->name, has_lock);
- mod_timer(&expire_timer, jiffies + has_lock);
- } else {
- if (del_timer(&expire_timer))
- if (debug_mask & DEBUG_EXPIRE)
- pr_info("wake_unlock: %s, stop expire "
- "timer\n", lock->name);
- if (has_lock == 0){
- <span style="color:#3366ff;"><strong>queue_work(suspend_work_queue, &suspend_work);</strong></span>
- }
- }
- if (lock == &main_wake_lock) {
- if (debug_mask & DEBUG_SUSPEND)
- print_active_locks(WAKE_LOCK_SUSPEND);
- #ifdef CONFIG_WAKELOCK_STAT
- update_sleep_wait_stats_locked(0);
- #endif
- }
- }
- spin_unlock_irqrestore(&list_lock, irqflags);
- }
- EXPORT_SYMBOL(wake_unlock);
在suspend()函数中,先判断当前是否有wake_lock,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。
- linux_source/kernel/power/wakelock.c
- static void suspend(struct work_struct *work)
- {
- int ret;
- int entry_event_num;
- struct timespec ts_entry, ts_exit;
- if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
- if (debug_mask & DEBUG_SUSPEND)
- pr_info("suspend: abort suspend\n");
- return;
- }
- entry_event_num = current_event_num;
- sys_sync();
- if (debug_mask & DEBUG_SUSPEND)
- pr_info("suspend: enter suspend\n");
- getnstimeofday(&ts_entry);
- <span style="color:#3366ff;"><strong>ret = pm_suspend(requested_suspend_state);</strong></span>
- getnstimeofday(&ts_exit);
- if (debug_mask & DEBUG_EXIT_SUSPEND) {
- struct rtc_time tm;
- rtc_time_to_tm(ts_exit.tv_sec, &tm);
- pr_info("suspend: exit suspend, ret = %d "
- "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);
- }
- if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {
- ++suspend_short_count;
- if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {
- suspend_backoff();
- suspend_short_count = 0;
- }
- } else {
- suspend_short_count = 0;
- }
- if (current_event_num == entry_event_num) {
- if (debug_mask & DEBUG_SUSPEND)
- pr_info("suspend: pm_suspend returned with no event\n");
- wake_lock_timeout(&unknown_wakeup, HZ / 2);
- }
- }
- static DECLARE_WORK(suspend_work, suspend); //stone tag
在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程。
- linux_source/kernel/power/suspend.c
- /**
- * pm_suspend - Externally visible function for suspending system.
- * @state: Enumerated value of state to enter.
- *
- * Determine whether or not value is within range, get state
- * structure, and enter (above).
- */
- int pm_suspend(suspend_state_t state)
- {
- if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX){
- return <span style="color:#3366ff;"><strong>enter_state(state);</strong></span>
- }
- return -EINVAL;
- }
- EXPORT_SYMBOL(pm_suspend);
在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。
- linux_source/kernel/power/suspend.c
- /**
- * enter_state - Do common work of entering low-power state.
- * @state: pm_state structure for state we're entering.
- *
- * Make sure we're the only ones trying to enter a sleep state. Fail
- * if someone has beat us to it, since we don't want anything weird to
- * happen when we wake up.
- * Then, do the setup for suspend, enter the state, and cleaup (after
- * we've woken up).
- */
- 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]);
- <span style="color:#3366ff;"><strong>error = suspend_prepare();</strong> </span>
- if (error)
- goto Unlock;
- if (suspend_test(TEST_FREEZER))
- goto Finish;
- pr_debug("PM: Entering %s sleep\n", pm_states[state]);
- pm_restrict_gfp_mask();
- <span style="color:#3366ff;"><strong>error = suspend_devices_and_enter(state);</strong></span>
- pm_restore_gfp_mask();
- Finish:
- pr_debug("PM: Finishing wakeup.\n");
- <span style="color:#3366ff;"><strong>suspend_finish();</strong></span>
- Unlock:
- mutex_unlock(&pm_mutex);
- return error;
- }
在suspend_prepare()函数中,先通过pm_prepare_console();给suspend分配一个虚拟终端来输出信息,再广播一个系统进入suspend的通报,关闭用户态的helper进程,然后调用suspend_freeze_processes()来冻结进程,最后会尝试释放一些内存。
- linux_source/kernel/power/suspend.c
- /**
- * suspend_prepare - Do prep work before entering low-power state.
- *
- * This is common code that is called for each state that we're entering.
- * Run suspend notifiers, allocate a console and stop all processes.
- */
- 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;
- error = usermodehelper_disable();
- if (error)
- goto Finish;
- <span style="color:#3366ff;"><strong>error = suspend_freeze_processes();</strong></span>
- if (!error)
- return 0;
- suspend_thaw_processes();
- usermodehelper_enable();
- Finish:
- pm_notifier_call_chain(PM_POST_SUSPEND);
- pm_restore_console();
- return error;
- }
在suspend_freeze_processes()函数中调用了freeze_processes()函数,而freeze_processes()函数中又调用了try_to_freeze_tasks()来完成冻结任务。在冻结过程中,会判断当前进程是否有wake_lock,若有,则冻结失败,函数会放弃冻结。
- linux_source/kernel/power/power.h
- static inline int suspend_freeze_processes(void)
- {
- return freeze_processes(); //stone tag
- }
- linux_source/kernel/power/process.c
- /**
- * freeze_processes - tell processes to enter the refrigerator
- */
- int freeze_processes(void)
- {
- int error;
- printk("Freezing user space processes ... ");
- <span style="color:#3366ff;"><strong>error = try_to_freeze_tasks(true);</strong></span>
- if (error)
- goto Exit;
- printk("done.\n");
- printk("Freezing remaining freezable tasks ... ");
- <span style="color:#3366ff;"><strong>error = try_to_freeze_tasks(false);</strong></span>
- if (error)
- goto Exit;
- printk("done.");
- oom_killer_disable();
- Exit:
- BUG_ON(in_atomic());
- printk("\n");
- return error;
- }
到现在,所有的进程(也包括workqueue/kthread) 都已经停止了,内核态进程有可能在停止的时候握有一些信号量,所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁, 所以在外设suspend()函数里面作lock/unlock锁要非常小心,建议不要在外设的suspend()里面等待锁。而且suspend的过程中,有一些log是无法输出的,所以一旦出现问题,非常难调试。
回到enter_state()函数中,在冻结进程完成后,调用suspend_devices_and_enter()函数让外设进入休眠。该函数中,首先休眠串口(之后不能再显示log,解决方法为在kernel配置选项的cmd_line中,添加”no_console_suspend”选项,或者直接注释掉suspend_console()函数),再通过dpm_suspend_start(PMSG_SUSPEND);函数调用各驱动的suspend函数。
- linux_source/kernel/power/suspend.c
- /**
- * suspend_devices_and_enter - suspend devices and enter the desired system
- * sleep state.
- * @state: state to enter
- */
- int suspend_devices_and_enter(suspend_state_t state)
- {
- int error;
- if (!suspend_ops)
- return -ENOSYS;
- trace_machine_suspend(state);
- if (suspend_ops->begin) {
- error = suspend_ops->begin(state);
- if (error)
- goto Close;
- }
- <span style="color:#ff6666;"><strong>//suspend_console();
- </strong></span> suspend_test_start();
- <span style="color:#3366ff;"><strong>error = dpm_suspend_start(PMSG_SUSPEND);</strong></span>
- 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;
- <span style="color:#3366ff;"><strong>error = suspend_enter(state);</strong></span>
- Resume_devices:
- suspend_test_start();
- <span style="color:#3366ff;"><strong>dpm_resume_end(PMSG_RESUME);</strong></span>
- suspend_test_finish("resume devices");
- <span style="color:#ff6666;"><strong>//resume_console();</strong></span>
- Close:
- if (suspend_ops->end)
- suspend_ops->end();
- trace_machine_suspend(PWR_EVENT_EXIT);
- return error;
- Recover_platform:
- if (suspend_ops->recover)
- suspend_ops->recover();
- goto Resume_devices;
- }
当外设进入休眠后,进入suspend_enter(),在suspend_enter()中suspend_ops->prepare()被调用,suspend_ops是板级的PM操作,依赖于具体的平台,在我samsung4412系统中,其注册在linux_source/arch/arm/plat-samsung/pm.c中,定义了suspend_ops->enter()函数。
- linux_source/arch/arm/plat-samsung/pm.c
- static const struct platform_suspend_ops s3c_pm_ops = {
- .enter = s3c_pm_enter,
- .prepare = s3c_pm_prepare,
- .finish = s3c_pm_finish,
- .valid = suspend_valid_only_mem,
- };
接下来,首先关闭IRQ,关闭不用的CPU,休眠所有的系统设备和总线。最后调用 suspend_pos->enter() 来使CPU进入省电状态。这时候,整个休眠过程完成,代码的执行也就停在这里了。
- linux_source/kernel/power/suspend.c
- /**
- * suspend_enter - enter the desired system sleep state.
- * @state: state to enter
- *
- * This function should be called after devices have been suspended.
- */
- static int suspend_enter(suspend_state_t state)
- {
- int error;
- if (suspend_ops->prepare) {
- error = suspend_ops->prepare();
- if (error)
- goto Platform_finish;
- }
- <span style="color:#3366ff;"><strong>error = dpm_suspend_noirq(PMSG_SUSPEND);</strong></span>
- if (error) {
- printk(KERN_ERR "PM: Some devices failed to power down\n");
- goto Platform_finish;
- }
- if (suspend_ops->prepare_late) {
- error = suspend_ops->prepare_late();
- if (error)
- goto Platform_wake;
- }
- if (suspend_test(TEST_PLATFORM))
- goto Platform_wake;
- <span style="color:#3366ff;"><strong>error = disable_nonboot_cpus();</strong>
- </span> if (error || suspend_test(TEST_CPUS))
- goto Enable_cpus;
- arch_suspend_disable_irqs();
- BUG_ON(!irqs_disabled());
- error = syscore_suspend();
- if (!error) {
- if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
- <strong><span style="color:#3366ff;">error = suspend_ops->enter(state);</span></strong>
- events_check_enabled = false;
- }
- <span style="color:#3366ff;"><strong>syscore_resume();</strong></span>
- }
- arch_suspend_enable_irqs();
- BUG_ON(irqs_disabled());
- Enable_cpus:
- enable_nonboot_cpus();
- Platform_wake:
- if (suspend_ops->wake)
- suspend_ops->wake();
- <span style="color:#3366ff;"><strong>dpm_resume_noirq(PMSG_RESUME);</strong>
- </span>
- Platform_finish:
- if (suspend_ops->finish)
- <span style="color:#3366ff;"><strong>suspend_ops->finish();</strong></span>
- return error;
- }
在suspend_pos->enter()所对应的函数中,代码最终停止在s3c_pm_arch_stop_clocks();处。
- linux_source/arch/arm/plat-samsung/pm.c
- /* s3c_pm_enter
- *
- * central control for sleep/resume process
- */
- static int s3c_pm_enter(suspend_state_t state)
- {
- .....
- /* save all necessary core registers not covered by the drivers */
- s3c_pm_save_gpios();
- s3c_pm_saved_gpios();
- s3c_pm_save_uarts();
- s3c_pm_save_core();
- .....
- /* send the cpu to sleep... */
- <span style="color:#3366ff;"><strong>s3c_pm_arch_stop_clocks();</strong></span>
- /* s3c_cpu_save will also act as our return point from when
- * we resume as it saves its own register state and restores it
- * during the resume. */
- s3c_cpu_save(0, PLAT_PHYS_OFFSET - PAGE_OFFSET);
- /* restore the cpu state using the kernel's cpu init code. */
- <span style="color:#3366ff;"><strong>cpu_init();</strong></span>
- s3c_pm_restore_core();
- s3c_pm_restore_uarts();
- s3c_pm_restore_gpios();
- s3c_pm_restored_gpios();
- .....
- return 0;
- }
Android 唤醒过程如下:
如果在休眠中系统被中断或者其他事件唤醒,接下来的代码就从suspend完成的地方开始执行,即pm.c中的s3c_pm_enter()中的cpu_init(),然后执行suspend_enter()的syscore_resume()函数,唤醒系统设备和总线,使能系统中断。然后回到suspend_devices_and_enter()函数中,dpm_resume_end(PMSG_RESUME)发出resume消息唤醒每个设备,使能终端。当suspend_devices_and_enter()执行完成后,系统外设已经唤醒,但进程依然是冻结的状态,返回到enter_state函数中,调用suspend_finish()函数。在suspend_finish()函数中,解冻进程和任务,使能用户空间helper进程,广播一个系统从suspend状态退出的notify,唤醒终端。
- linux_source/kernel/power/suspend.c
- /**
- * suspend_finish - Do final work before exiting suspend sequence.
- *
- * Call platform code to clean up, restart processes, and free the
- * console that we've allocated. This is not called for suspend-to-disk.
- */
- static void suspend_finish(void)
- {
- suspend_thaw_processes();
- usermodehelper_enable();
- <span style="color:#3366ff;"><strong>pm_notifier_call_chain(PM_POST_SUSPEND); //重要,唤醒系统和RIL等进程</strong>
- </span> pm_restore_console();
- }
当所有的唤醒已经结束以后,用户进程都已经开始运行了,但没点亮屏幕,唤醒通常会是以下的几种原因:
如果是来电,那么Modem会通过中断脚唤醒AP,rild是一个守护进程,在醒来后不断从modem usb数据端口polling数据,当发现是incoming call或者incoming sms事件后就通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写”on”到 /sys/power/state 来调用late resume(),执行点亮屏幕等操作。
用户按键事件会送到WindowManager中,WindowManager会处理这些按键事件,按键分为几种情况,如果按键不是唤醒键,那么WindowManager会主动放弃wakeLock来使系统进入再次休眠;如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行late Resume。 当”on”被写入到/sys/power/state之后,同early_suspend过程,request_suspend_state()被调用,只是执行的工作队列变为late_resume_work。在late_resume函数中,唤醒调用了early_suspend的设备。
- suspend-resume(3)
- suspend/resume
- resume suspend wait sleep
- android suspend/resume
- linux kernel suspend Resume
- android suspend and resume
- Andriod PM suspend&resume
- Suspend&Resume流程
- USB 2.0 suspend resume
- usb suspend/resume
- 说说usb suspend/resume
- resume suspend wait sleep
- Linux suspend&resume
- android suspend/resume(1)
- suspend-resume(2)
- suspend-resume(4)
- resume suspend wait sleep
- suspend和resume弊端
- Java正则表达式
- 最小化托盘的实现方法
- 我的Foxit Reader也能用Vim风格的快捷键了!!
- 《浪潮之巅(上)》阅读笔记
- leetcode:Linked List Cycle II
- suspend-resume(3)
- MySql MediumBlob
- C++单例模式
- Redis过期键
- jQuery html5Validate基于HTML5表单验证插件
- Codeforces Round #277.5 (Div. 2)(C题)
- Codeforces Round #277.5 (Div. 2)
- 关于销售订单高级定价的一点疑惑
- linux下更新python版本