当前位置:   article > 正文

wakeup_sources

wakeup_sources

android的耗电问题,会涉及到wakelock部分。
https://blog.csdn.net/weixin_42322147/article/details/80469590

/**
 * 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 source was activated.
 * @relax_count: Number of times the wakeup source 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;
	spinlock_t		lock;
	struct timer_list	timer;
	unsigned long		timer_expires;
	ktime_t total_time; //wakeup source处于active的总时长
	ktime_t max_time;
	ktime_t last_time;
	ktime_t start_prevent_time;
	ktime_t prevent_sleep_time;
	unsigned long		event_count; 
	unsigned long		active_count;
	unsigned long		relax_count;
	unsigned long		expire_count;
	unsigned long		wakeup_count;
	bool			active:1;
	bool			autosleep_enabled:1;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

在root权限下,可以通过查看/d/wakeup_sources来查看wakelock的情况。

name active_count event_count wakeup_count expire_count active_since total_time
max_time last_change prevent_suspend_time
bluetooth_timer 64 64 0 0 0 3213 3001 69776 0

wakeup_sources这个节点的信息对分析耗电比较有用的数据有:
active_count, active_since, total_time。

active_count:上锁的次数
active_since:当前的wakelock已经持续的时间
total_time:这个锁开机以来一共lock的时间

上文中active_count=64次
上文中active_since=3213毫秒
上文中total_time=3001毫秒

当CPU无法睡下去时,很可能就是因为某个driver持有wakelock不放导致的。
这时可以从这个节点来分析,找出根源。
不过,这个节点只有root权限才能查看,这是限制条件。

一、 节点的创建
根据节点的名称,先查看节点生成的文件为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);    //创建"/d/wakeup_sources"。
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5

二、节点操作的相关函数
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,
};

三、cat /d/wakeup_sources时调用的函数,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);
}
  • 1
  • 2
  • 3

再看看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); //打印出这个ws的相关信息
	rcu_read_unlock();
 
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

再看看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_time
	max_time = ws->max_time;
	prevent_sleep_time = ws->prevent_sleep_time;
	active_count = ws->active_count;
	if (ws->active) {    //如果这个wakelock还在,没有释放掉
		ktime_t now = ktime_get();
 
		active_time = ktime_sub(now, ws->last_time); //active_time就是开始上锁到目前时间差
		total_time = ktime_add(total_time, active_time);  // total_time 就是上一次的total_time加上active_time
		if (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));     //结果
 
	spin_unlock_irqrestore(&ws->lock, flags);
 
	return ret;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

四、wakeup_sources,一个当前文件中的全局list。
在init一个wakelock的时候添加。
上面遍历的 wakeup_sources,本文件中的一个全局链表。
很容易猜测到,每次申请一个wake_lock时,都会添加一个item到这个链表中。
看看这个全局的list的声明和初始化。

static LIST_HEAD(wakeup_sources); //初始化。
接下来看这个list的添加。想要使用wakelock,肯定是要先调用下面的init函数。我们在kernel中声明的wake_lock结构都有wakeup_source结构成员。后面更多的是用这个wakeup_source结构来管理。

static inline void wake_lock_init(struct wake_lock *lock, int type, const char *name) {
	wakeup_source_init(&lock->ws, name);
}
  • 1
  • 2
  • 3

接着看被调用的wakeup_source_init。

static inline void wakeup_source_init(struct wakeup_source *ws, const char *name) {
	wakeup_source_prepare(ws, name); //准备工作
	wakeup_source_add(ws); //真正的添加。
}
  • 1
  • 2
  • 3
  • 4

继续看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;  //初始化为非active
	ws->last_time = ktime_get();   //当前时间。
 
	spin_lock_irqsave(&events_lock, flags);
	list_add_rcu(&ws->entry, &wakeup_sources);  //添加到wakeup_sources
	spin_unlock_irqrestore(&events_lock, flags);
}
EXPORT_SYMBOL_GPL(wakeup_source_add);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

五、wakelock上锁时的操作
上锁时,都会调用下面的wake_lock函数。

static inline void wake_lock(struct wake_lock *lock){
	__pm_stay_awake(&lock->ws);
}
  • 1
  • 2
  • 3

继续看__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);  //主要函数。
	del_timer(&ws->timer);
	ws->timer_expires = 0;
 
	spin_unlock_irqrestore(&ws->lock, flags);
}
EXPORT_SYMBOL_GPL(__pm_stay_awake);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

继续看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计数
	/* This is racy, but the counter is approximate anyway. */
	if (events_check_enabled)
		ws->wakeup_count++;//wakeup_count计数
	if (!ws->active)  //如果是非active状态
		wakeup_source_activate(ws); //那就变成active
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

接着看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不会睡下去
 
	ws->active = true;
	ws->active_count++; //active_count计数
	ws->last_time = ktime_get();  //这时的时间,也就是开始上锁的时间
	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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

六、wakelock释放时的操作
在kernel wakelock释放的时候,都会调用下面的wake_unlock函数。

static inline void wake_unlock(struct wake_lock *lock)
{
	__pm_relax(&lock->ws);
}
继续看看__pm_relax。
  • 1
  • 2
  • 3
  • 4
  • 5
/**
 * __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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

再看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);   //完整上锁的时间
	ws->total_time = ktime_add(ws->total_time, duration); //这把所从init之后所有上锁的时间总和
	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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/818039
推荐阅读
  

闽ICP备14008679号