Android kernel中wakeup_sources解析
前些時(shí)間,在學(xué)習(xí)android的耗電問題。不可避免,會(huì)涉及到wakelock的部分。
在root權(quán)限下,可以通過查看/d/wakeup_sources來查看wakelock的情況。
name active_count event_count wakeup_count expire_count active_since total_time max_time last_change prevent_suspend_time HVDCPD_WL 18 18 0 0 0 100 11 76513 0 ipc000000ef_sensors@1.0-ser 2536 2891 0 0 0 483 6 85772 0 ipc000000ee_sensors@1.0-ser 3 3 0 0 0 0 0 45133 0 ipc000000ed_sensors@1.0-ser 9 9 0 0 0 8 6 84263 0 bluetooth_timer 64 64 0 0 0 3213 3001 69776 0 hal_bluetooth_lock 1 1 0 0 0 79 79 33380 0 SMD_TTY_APPS_RIVA_BT_ACL_RA 0 0 0 0 0 0 0 33297 0 APPS_RIVA_BT_ACL 0 0 0 0 0 0 0 33297 0 SMD_TTY_APPS_RIVA_BT_CMD_RA 109 114 0 0 0 4 0 69775 0那后面就通過code來分析一下這個(gè)節(jié)點(diǎn),也看看什么參數(shù)對(duì)分析耗電比較重要。
1. 節(jié)點(diǎn)的創(chuàng)建
根據(jù)節(jié)點(diǎn)的名稱,先查看節(jié)點(diǎn)生成的文件為kernel/msm-3.18/drivers/base/power/wakeup.c
static int __init wakeup_sources_debugfs_init(void) {wakeup_sources_stats_dentry = debugfs_create_file("wakeup_sources",S_IRUGO, NULL, NULL, &wakeup_sources_stats_fops); //創(chuàng)建"/d/wakeup_sources"。return 0; }2.節(jié)點(diǎn)操作的相關(guān)函數(shù)
static const struct file_operations wakeup_sources_stats_fops = {.owner = THIS_MODULE,.open = wakeup_sources_stats_open,.read = seq_read,.llseek = seq_lseek,.release = single_release, };3.cat /d/wakeup_sources時(shí)調(diào)用的函數(shù),wakeup_sources_stats_open
static int wakeup_sources_stats_open(struct inode *inode, struct file *file) {return single_open(file, wakeup_sources_stats_show, NULL); }再看看wakeup_sources_stats_show。
/*** wakeup_sources_stats_show - Print wakeup sources statistics information.* @m: seq_file to print the statistics into.*/ static int wakeup_sources_stats_show(struct seq_file *m, void *unused) {struct wakeup_source *ws;seq_puts(m, "name\t\t\t\t\tactive_count\tevent_count\twakeup_count\t""expire_count\tactive_since\ttotal_time\tmax_time\t""last_change\tprevent_suspend_time\n"); //這就是上面的第一行。rcu_read_lock(); list_for_each_entry_rcu(ws, &wakeup_sources, entry) //互斥遍歷wakeup_sources,每次取出其中之一ws.print_wakeup_source_stats(m, ws); //打印出照這個(gè)ws的相關(guān)信息rcu_read_unlock();return 0; }再看看print_wakeup_source_stats:
/*** print_wakeup_source_stats - Print wakeup source statistics information.* @m: seq_file to print the statistics into.* @ws: Wakeup source object to print the statistics for.*/ static int print_wakeup_source_stats(struct seq_file *m,struct wakeup_source *ws) {unsigned long flags;ktime_t total_time;ktime_t max_time;unsigned long active_count;ktime_t active_time;ktime_t prevent_sleep_time;int ret;spin_lock_irqsave(&ws->lock, flags);total_time = ws->total_time; //之前的total_timemax_time = ws->max_time;prevent_sleep_time = ws->prevent_sleep_time;active_count = ws->active_count;if (ws->active) { //如果這個(gè)wakelock還在,沒有釋放掉ktime_t now = ktime_get();active_time = ktime_sub(now, ws->last_time); //active_time就是開始上鎖到目前時(shí)間差total_time = ktime_add(total_time, active_time); // total_time 就是上一次的total_time加上active_timeif (active_time.tv64 > max_time.tv64)max_time = active_time;if (ws->autosleep_enabled)prevent_sleep_time = ktime_add(prevent_sleep_time,ktime_sub(now, ws->start_prevent_time));} else {active_time = ktime_set(0, 0);}ret = seq_printf(m, "%-32s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t""%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n",ws->name, active_count, ws->event_count,ws->wakeup_count, ws->expire_count,ktime_to_ms(active_time), ktime_to_ms(total_time),ktime_to_ms(max_time), ktime_to_ms(ws->last_time),ktime_to_ms(prevent_sleep_time)); //結(jié)果。spin_unlock_irqrestore(&ws->lock, flags);return ret; }4.?wakeup_sources,一個(gè)當(dāng)前文件中的全局list。在init一個(gè)wakelock的時(shí)候添加。
上面遍歷的 wakeup_sources,本文件中的一個(gè)全局鏈表。很容易猜測(cè)到,每次申請(qǐng)一個(gè)wake_lock時(shí),都會(huì)添加一個(gè)item到這個(gè)鏈表中。看看這個(gè)全局的list的聲明和初始化。
static LIST_HEAD(wakeup_sources); //初始化。接下來看這個(gè)list的添加。想要使用wakelock,肯定是要先調(diào)用下面的init函數(shù)。我們?cè)趉ernel中聲明的wake_lock結(jié)構(gòu)都有wakeup_source結(jié)構(gòu)成員。后面更多的是用這個(gè)wakeup_source結(jié)構(gòu)來管理。
static inline void wake_lock_init(struct wake_lock *lock, int type, ?const char *name) { wakeup_source_init(&lock->ws, name); }接著看被調(diào)用的wakeup_source_init。
static inline void wakeup_source_init(struct wakeup_source *ws,const char *name) {wakeup_source_prepare(ws, name); //準(zhǔn)備工作wakeup_source_add(ws); //真正的添加。 }繼續(xù)看wakeup_source_add.
/*** wakeup_source_add - Add given object to the list of wakeup sources.* @ws: Wakeup source object to add to the list.*/ void wakeup_source_add(struct wakeup_source *ws) {unsigned long flags;if (WARN_ON(!ws))return;spin_lock_init(&ws->lock);setup_timer(&ws->timer, pm_wakeup_timer_fn, (unsigned long)ws);ws->active = false; //初始化為非activews->last_time = ktime_get(); //當(dāng)前時(shí)間。spin_lock_irqsave(&events_lock, flags);list_add_rcu(&ws->entry, &wakeup_sources); //添加到wakeup_sourcesspin_unlock_irqrestore(&events_lock, flags); } EXPORT_SYMBOL_GPL(wakeup_source_add);5.wakelock上鎖時(shí)的操作
上鎖時(shí),都會(huì)調(diào)用下面的wake_lock函數(shù)。
static inline void wake_lock(struct wake_lock *lock) {__pm_stay_awake(&lock->ws); }繼續(xù)看__pm_stay_awake。
/*** __pm_stay_awake - Notify the PM core of a wakeup event.* @ws: Wakeup source object associated with the source of the event.** It is safe to call this function from interrupt context.*/ void __pm_stay_awake(struct wakeup_source *ws) {unsigned long flags;if (!ws)return;spin_lock_irqsave(&ws->lock, flags);wakeup_source_report_event(ws); //主要函數(shù)。del_timer(&ws->timer);ws->timer_expires = 0;spin_unlock_irqrestore(&ws->lock, flags); } EXPORT_SYMBOL_GPL(__pm_stay_awake);繼續(xù)看wakeup_source_report_event。
/*** wakeup_source_report_event - Report wakeup event using the given source.* @ws: Wakeup source to report the event for.*/ static void wakeup_source_report_event(struct wakeup_source *ws) {ws->event_count++; //event_count計(jì)數(shù)/* This is racy, but the counter is approximate anyway. */if (events_check_enabled)ws->wakeup_count++;//wakeup_count計(jì)數(shù)if (!ws->active) //如果是非active狀態(tài)wakeup_source_activate(ws); //那就變成active }接著看wakeup_source_activate.
/*** 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;/** active wakeup source should bring the system* out of PM_SUSPEND_FREEZE state*/freeze_wake(); //保證上鎖期間CPU不會(huì)睡下去ws->active = true;ws->active_count++; //active_count計(jì)數(shù)ws->last_time = ktime_get(); //這時(shí)的時(shí)間,也就是開始上鎖的時(shí)間if (ws->autosleep_enabled)ws->start_prevent_time = ws->last_time;/* Increment the counter of events in progress. */cec = atomic_inc_return(&combined_event_count);trace_wakeup_source_activate(ws->name, cec); }6.wakelock釋放時(shí)的操作
在kernel wakelock釋放的時(shí)候,都會(huì)調(diào)用下面的wake_unlock函數(shù)。
static inline void wake_unlock(struct wake_lock *lock) {__pm_relax(&lock->ws); } 繼續(xù)看看__pm_relax。 /*** __pm_relax - Notify the PM core that processing of a wakeup event has ended.* @ws: Wakeup source object associated with the source of the event.** Call this function for wakeup events whose processing started with calling* __pm_stay_awake().** It is safe to call it from interrupt context.*/ void __pm_relax(struct wakeup_source *ws) {unsigned long flags;if (!ws)return;spin_lock_irqsave(&ws->lock, flags);if (ws->active) //如果目前仍是active,那就要釋放掉wakeup_source_deactivate(ws);spin_unlock_irqrestore(&ws->lock, flags); } EXPORT_SYMBOL_GPL(__pm_relax);再看wakeup_source_deactivate。
/*** 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); //完整上鎖的時(shí)間ws->total_time = ktime_add(ws->total_time, duration); //這把所從init之后所有上鎖的時(shí)間總和if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))ws->max_time = duration;ws->last_time = now;del_timer(&ws->timer);ws->timer_expires = 0;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);split_counters(&cnt, &inpr);if (!inpr && waitqueue_active(&wakeup_count_wait_queue))wake_up(&wakeup_count_wait_queue); }7. 總結(jié)
wakeup_sources這個(gè)節(jié)點(diǎn)的信息對(duì)分析耗電比較有用的數(shù)據(jù)有active_count, active_since,total_time。
active_count--上鎖的次數(shù)
active_since--當(dāng)前的wakelock已經(jīng)持續(xù)的時(shí)間
total_time--這個(gè)鎖開機(jī)以來一共lock的時(shí)間
當(dāng)CPU無法睡下去時(shí),很可能就是因?yàn)槟硞€(gè)driver持有wakelock不放導(dǎo)致的。這時(shí)可以這個(gè)節(jié)點(diǎn)來分析,找出根源。
不過,這個(gè)節(jié)點(diǎn)只有root權(quán)限才能查看,這是限制條件。
總結(jié)
以上是生活随笔為你收集整理的Android kernel中wakeup_sources解析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 【JVM】jvm的双亲委派机制
- 下一篇: 一分钟带你解读光纤收发器,秒懂(一)
