一:wakeup_source简介: linux 3.4内核PM使用了wakeup_source来保持唤醒状态,也就是keep awake。之前android一直是基于Linux加入了wake_lock机制来阻止系统休眠,后来Linux 3.4内核加入了wakeup_source来管理,安卓4.4跟着升级内核也就摒弃了自己的繁杂的wake_lock机制,在对上层接口并不改变,在内核wake_lock实现直接基于wakeup_source来实现的。当然也会带来debug上的一些问题,比如以前的wake_lock自身带有强大的debug信息,那么我们在调试的时候可以自己看见dmesg中默认打印active wake lock XXX,很直观来辨别需要休眠的时候那个wake lock有问题阻止了休眠。这个需要我们自己来完善。个人认为改进很大,现在使用了autosleep机制,只要不存在任何active wakeup_source了,系统自动休眠,当有active wake_source自动block住,个人认为休眠更及时,非休眠时间在减少,同时不会消耗额外的资源。使用基于queue work与进程block来管理suspend。还有这里的wakeup_source个人觉得应该叫keepawake_source或者stayawake_souce,毕竟系统的唤醒也就是cpu的再次运行是由中断唤醒的而不是wakeup_source。同时安卓4.4还有一个重大改变就是去除了early suspend机制改为fb event通知机制。那么现在就只有suspend与resume,runtime suspend与runtime resume了。
/** * struct wakeup_source - Representation of wakeup sources * * @total_time: Total time this wakeup source has been active. * @max_time: Maximum time this wakeup source has been continuously active. * @last_time: Monotonic clock when the wakeup source's was touched last time. * @prevent_sleep_time: Total time this source has been preventing autosleep. * @event_count: Number of signaled wakeup events. * @active_count: Number of times the wakeup sorce was activated. * @relax_count: Number of times the wakeup sorce was deactivated. * @expire_count: Number of times the wakeup source's timeout has expired. * @wakeup_count: Number of times the wakeup source might abort suspend. * @active: Status of the wakeup source. * @has_timeout: The wakeup source has been activated with a timeout. */ struct wakeup_source { const char *name; struct list_head entry; struct list_head list; spinlock_t lock; struct timer_list timer; unsigned long timer_expires; //超时时间,也就是wake_lock_timeout()里面的时间参数,超时后会执行deactivate函数 ktime_t total_time; ktime_t max_time; ktime_t last_time; ktime_t start_prevent_time; ktime_t prevent_sleep_time; unsigned long event_count; //event计数 unsigned long active_count;//active计数 unsigned long relax_count; unsigned long expire_count; unsigned long wakeup_count; bool active:1; //用于判断是否是active状态 bool autosleep_enabled:1;//这个变量是来标记active等时间的 };
//active任何wakeup_source都会执行该函数,标记active为true /** * wakup_source_activate - Mark given wakeup source as active. * @ws: Wakeup source to handle. * * Update the @ws' statistics and, if @ws has just been activated, notify the PM * core of the event by incrementing the counter of of wakeup events being * processed. */ static void wakeup_source_activate(struct wakeup_source *ws) { unsigned int cec;
/* Increment the counter of events in progress. */ cec = atomic_inc_return(&combined_event_count);
trace_wakeup_source_activate(ws->name, cec); }
//deactivate任何wakeup_source都会执行该函数,标记active为false /** * wakup_source_deactivate - Mark given wakeup source as inactive. * @ws: Wakeup source to handle. * * Update the @ws' statistics and notify the PM core that the wakeup source has * become inactive by decrementing the counter of wakeup events being processed * and incrementing the counter of registered wakeup events. */ static void wakeup_source_deactivate(struct wakeup_source *ws) { unsigned int cnt, inpr, cec; ktime_t duration; ktime_t now;
ws->relax_count++; /* * __pm_relax() may be called directly or from a timer function. * If it is called directly right after the timer function has been * started, but before the timer function calls __pm_relax(), it is * possible that __pm_stay_awake() will be called in the meantime and * will set ws->active. Then, ws->active may be cleared immediately * by the __pm_relax() called from the timer function, but in such a * case ws->relax_count will be different from ws->active_count. */ if (ws->relax_count != ws->active_count) { ws->relax_count--; return; }
ws->active = false;
now = ktime_get(); duration = ktime_sub(now, ws->last_time); ws->total_time = ktime_add(ws->total_time, duration); if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time)) ws->max_time = duration;
if (ws->autosleep_enabled) update_prevent_sleep_time(ws, now);
/* * Increment the counter of registered wakeup events and decrement the * couter of wakeup events in progress simultaneously. */ cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count); trace_wakeup_source_deactivate(ws->name, cec);
if (!pm_get_wakeup_count(&final_count, false))//获取final_count,非block,当然也会检查是否有active wakeup source,当有active存在再次queue work goto out;
/* * If the wakeup occured for an unknown reason, wait to prevent the * system from trying to suspend and waking up in a tight loop. */ if (final_count == initial_count) //这里遇见未知原因,initial_count与final_count相等,超时500ms后继续往下执行。这种现象我也是跟了许久没有遇见过。 schedule_timeout_uninterruptible(HZ / 2);
out: queue_up_suspend_work(); //调度queue work会再次执行该函数,实际上只要一次echo mem > sys/power/autosleep后这个进程一直会在auto_sleep cycle。 }
pm_get_wakeup_count原型:
bool pm_get_wakeup_count(unsigned int *count, bool block) { unsigned int cnt, inpr;
if (block) { //当block为真时,该进程可能会block住 DEFINE_WAIT(wait);
看下面的dmesg: <6>[ 928.536418] CPU0: msm_cpu_pm_enter_sleep mode 000000,00000000,00000000,00000000,00000000,00000020,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000 <6>[ 928.543066] PM: noirq resume of devices complete after 6.020 msecs <6>[ 928.548512] PM: early resume of devices complete after 2.598 msecs <6>[ 928.650793] PM: resume of devices complete after 102.266 msecs <6>[ 928.660290] Restarting tasks ... done. <6>[ 928.681208] PM: suspend exit 1970-01-05 05:23:32.206389881 UTC <6>[ 928.681229] active wake lock KeyEvents <6>[ 928.681267] active wake lock qpnp_soc_wake <6>[ 928.681284] active wake lock alarm, time left 486 <6>[ 928.681342] active wake lock KeyEvents <6>[ 928.681584] active wake lock qpnp_soc_wake <6>[ 928.681600] active wake lock alarm, time left 486 <6>[ 928.696345] request_suspend_state: wakeup at 928691792356 (1970-01-05 05:23:32.221521704 UTC) <6>[ 928.708608] mdss_dsi_panel_power on=1
/** * 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 (!suspend_ops) return -ENOSYS;
trace_machine_suspend(state); if (suspend_ops->begin) { error = suspend_ops->begin(state); if (error) goto Close; } suspend_console(); suspend_test_start(); error = dpm_suspend_start(PMSG_SUSPEND);//这里会执行所有driver的suspend函数,suspend里面有active wakeup_source或者return 为真的话,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;
do { error = suspend_enter(state, &wakeup);//这里会diable cpu } while (!error && !wakeup && suspend_ops->suspend_again && suspend_ops->suspend_again());