前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux电源管理-Autosleep

Linux电源管理-Autosleep

作者头像
DragonKingZhu
发布2020-03-24 17:19:42
2.7K0
发布2020-03-24 17:19:42
举报

前言

什么是Autosleep? 字面理解就是"没有事情干的时候睡觉"。而起初autosleep是在Android上的一个patch(https://lwn.net/Articles/479711/)演化而来的,当时名字叫做"Opportunistic sleep",翻译过来叫做"机会主义睡眠",也就是有机会就睡。因为此名字比较有争议,最后修改为autosleep。

autosleep一种强大的电源管理方法,只要在系统没有什么事情可做的时候,整个系统就睡眠下去。此机制在android手机上非常有效,同时也能阻止不良应用程序一直保持系统唤醒,浪费电池。但是重要的是: 如何判断当前系统没有事情可做,也就是如何判断系统在空闲状态?

这时候wakeup event framework的出现就可以解决此问题,当系统中没有wakeup event事件的时候,就尝试系统suspend。如果在suspend的过程中有wakeup event就接着resume系统即可。

同样autosleep.c的注释可以说明autosleep的前生是Opportunistic sleep。

代码语言:javascript
复制
/*
 * kernel/power/autosleep.c
 *
 * Opportunistic sleep support.
 *
 * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
 */

详细文章可见: https://lwn.net/Articles/479841/

Autosleep原理

1. 当系统没有任何事情做的时候,就尝试susupend。

2. 当系统中没有wakeup event事件发生的时候,就可以尝试suspend,需要wakeup event framework机制支持。

3. autosleep功能需要在kernel config中开启CONFIG_PM_AUTOSLEEP=y。

4. 通过写"mem, disk, standby, freeze"到/sys/power/autosleep可以开启autosleep。

5. 通过写"off"到/sys/power/autosleep就可以关闭autosleep。

流程分析

A: 通过执行"echo mem > /sys/power/autosleep"此命令,系统就会在没有事情可做的时候,选择执行suspend的流程。

代码语言:javascript
复制
static ssize_t autosleep_store(struct kobject *kobj,
			       struct kobj_attribute *attr,
			       const char *buf, size_t n)
{
	suspend_state_t state = decode_state(buf, n);
	int error;

	if (state == PM_SUSPEND_ON
	    && strcmp(buf, "off") && strcmp(buf, "off\n"))
		return -EINVAL;

	error = pm_autosleep_set_state(state);
	return error ? error : n;
}

通过判断当前state的状态,如果是off的话,直接返回,否则调用pm_autosleep_set_state函数。

代码语言:javascript
复制
int pm_autosleep_set_state(suspend_state_t state)
{

#ifndef CONFIG_HIBERNATION
	if (state >= PM_SUSPEND_MAX)
		return -EINVAL;
#endif

	__pm_stay_awake(autosleep_ws);

	mutex_lock(&autosleep_lock);

	autosleep_state = state;

	__pm_relax(autosleep_ws);

	if (state > PM_SUSPEND_ON) {
		pm_wakep_autosleep_enabled(true);
		queue_up_suspend_work();
	} else {
		pm_wakep_autosleep_enabled(false);
	}

	mutex_unlock(&autosleep_lock);
	return 0;
}

1. 判断state参数是否合法。

2. 调用__pm_stay_awake上报一个wakeup events,保持系统唤醒。

3. 使用mutex保护全局变量autosleep。

4. 使用参数state,修改autosleep_state

5. 调用__pm_relax释放wakeup events,系统可以睡眠。

6. 判断state的值,如果是off,调用pm_wakep_autosleep_enabled函数disable autosleep,否则调用pm_wakep_autosleep_enabled函数enable autosleep功能,同时提交工作队列。

代码语言:javascript
复制
void pm_wakep_autosleep_enabled(bool set)
{
	struct wakeup_source *ws;
	ktime_t now = ktime_get();

	rcu_read_lock();
	list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
		spin_lock_irq(&ws->lock);
		if (ws->autosleep_enabled != set) {
			ws->autosleep_enabled = set;
			if (ws->active) {
				if (set)
					ws->start_prevent_time = now;
				else
					update_prevent_sleep_time(ws, now);
			}
		}
		spin_unlock_irq(&ws->lock);
	}
	rcu_read_unlock();
}

1. 对系统中的所有wakeup source,修改autosleep_enable标志。

2. 如果此wakeup状态是active,且autosleep为enable的话,修改wakeup source的start_prevenet_time修改为现在,意味着从现在开始就已经阻止autosleep的功能。

3. 如果此wakeup状态是active,且autosleep为disable的话。说明在此wakeup source active的过程中,关闭了autosleep功能,更新总的阻止autosleep的时间。

代码语言:javascript
复制
void queue_up_suspend_work(void)
{
	if (autosleep_state > PM_SUSPEND_ON)
		queue_work(autosleep_wq, &suspend_work);
}

此时就需要将suspend_work挂入到autosleep_wq工作队列中,而autosleep_wq是在函数pm_autosleep_init函数中创建的。

代码语言:javascript
复制
int __init pm_autosleep_init(void)
{
	autosleep_ws = wakeup_source_register("autosleep");
	if (!autosleep_ws)
		return -ENOMEM;

	autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
	if (autosleep_wq)
		return 0;

	wakeup_source_unregister(autosleep_ws);
	return -ENOMEM;
}

1. 此函数会主次一个"autosleep" wakeupsource。用于在设置autosleep的状态时,保持系统处于唤醒状态。

2. 同时创建一个名字为"autosleep"的有序工作队列。为了保证一个时刻只能处理一个work。

B: 当提交suspend_work到工作队列之后,就会调用try_to_suspend函数查询是否可以进入suspend。

代码语言:javascript
复制
static void try_to_suspend(struct work_struct *work)
{
	unsigned int initial_count, final_count;

	if (!pm_get_wakeup_count(&initial_count, true))
		goto out;

	mutex_lock(&autosleep_lock);

	if (!pm_save_wakeup_count(initial_count) ||
		system_state != SYSTEM_RUNNING) {
		mutex_unlock(&autosleep_lock);
		goto out;
	}

	if (autosleep_state == PM_SUSPEND_ON) {
		mutex_unlock(&autosleep_lock);
		return;
	}
	if (autosleep_state >= PM_SUSPEND_MAX)
		hibernate();
	else
		pm_suspend(autosleep_state);

	mutex_unlock(&autosleep_lock);

	if (!pm_get_wakeup_count(&final_count, false))
		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)
		schedule_timeout_uninterruptible(HZ / 2);

 out:
	queue_up_suspend_work();
}

1. 调用pm_get_wakeup_count函数获取系统的wakeup count数量,存放到initial_count变量中。

2. 调用pm_save_wakeup_count函数存放到saved_count变量中,如果失败,说明有wakeup event发生。跳到out处重新提交,重新来过。

3. 根据autosleep_state的状态,进行相应的睡眠方式。

4. 在系统产生wakeup event之后,唤醒系统之后再次读取wakeup count的数量。

5. 如果读取数量和suspend保存的一样,说明有不明原因唤醒系统。这时候就会发生: autosleep->wakeup->autosleep->wakeup的循环中。所以此时try_to_suspend函数等待0.5s,再次尝试suspend。

C: 这时候系统就会在没事做的时候尝试suspend。

D: 如果有唤醒事件,系统就会从睡眠中唤醒。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Autosleep原理
  • 流程分析
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档