基于wakeup_source的linux autosleep分析
来源:互联网 发布:菜鸟网络郑州空港园区 编辑:程序博客网 时间:2024/06/06 17:38
1 引言
1.1 编写目的
分析Linux PM的框架及其具体实现
1.2 阅读建议
了解工作队列,等待队列,sys文件
1.3 参考资料
Linux 3.4
1.5 运行平台
Android 4.4
1.4 缩略语
PM:Power Management
2 总体描述
不管是嵌入式设备或者大型服务器,PM始终是Linux 必须关心的问题,因此一套成熟健壮的PM管理方案是必须的。目前大量手持设备都是基于Linux内核的,Android系统为了满足自身PM的需要,提出了wake_lock机制。但是这一直被Linux 主流所诟病,自从Linux3.4,负责Linux PM相关的Intel 员工 Rafael J. Wysocki <rjw@sisk.pl>在主流内核上面实现了基于wakeup_source的PM,并且兼容用户态继续使用wake_lock。
wakeup_source 定义的是唤醒事件,唤醒事件来源:内核产生,比如设备中断,rtc-alarm;用户态产生,避免自己在处理的时候,系统进入autosleep,用户进程被freeze。因此,autosleep就是一个优先级很低的任务了,只要一有wakeup_source产生,就不能进入睡眠或者已进入睡眠,就必须唤醒。Linux 实现周期性检测有内核定时器,工作队列。由于auto sleep优先级很低,目前采用工作队列机制。try_to_suspend这个工作项主要是读取目前的wakeup_count和in process count。如果in process count为0,说明现在很安静(很多唤醒事件都不产生了,这个概率其实很小),可以开始进入suspend。在进入suspend的时候,会保存wakeup_count,目的有两个:在进入suspend过程中,又有新的事件产生,就把每个事件的唤醒计数加1;在系统resume的时候,比较wake_count,如果保存事件和最新的事件计数不一致,说明产生了一个未知事件,让出cpu时间去处理这个未知事件。
用户态使用 echo mem > /sys/power/auto_sleep激活try_to_suspend或者,使能auto_sleep;由try_to_suspend检查当没有唤醒事件了,进入pm_suspend;当/sys/power/state为 on;echo mem > /sys/power/state,直接pm_suspend
3 设计描述
3.1 数据结构描述
struct wakeup_source {
const char *name;
struct list_head entry;
spinlock_t lock;
struct timer_list timer; //一些wakeup_source超时后,会变成deactive
unsigned long timer_expires; //超时时间
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; //产生事件计数
unsigned long active_count; //激活计数
unsigned long relax_count; //释放计数
unsigned long expire_count; //超时被变dactive计数
unsigned long wakeup_count; //当suspend后,唤醒系统计数
bool active:1;
bool autosleep_enabled:1; //事件是否允许系统autosleep
};
3.2 sys 接口描述
3.2.1 autosleep_show 显示autosleep状态 off表示不允许autosleep,mem表示允许
点击(此处)折叠或打开
static ssize_t autosleep_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
suspend_state_t state = pm_autosleep_state();
if (state == PM_SUSPEND_ON)
return sprintf(buf, "off\n");
#ifdef CONFIG_SUSPEND
if (state < PM_SUSPEND_MAX)
return sprintf(buf, "%s\n", valid_state(state) ?
pm_states[state] : "error");
#endif
#ifdef CONFIG_HIBERNATION
return sprintf(buf, "disk\n");
#else
return sprintf(buf, "error");
#endif
}
3.2.2 autosleep_store 设置
点击(此处)折叠或打开正常
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;
}
3.2.3 state_show打印出目前系统支持的autosleep状态
点击(此处)折叠或打开
tatic ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
#ifdef CONFIG_SUSPEND
int i;
for (i = 0; i < PM_SUSPEND_MAX; i++) {
if (pm_states[i] && valid_state(i))
s += sprintf(s,"%s ", pm_states[i]);
}
#endif
#ifdef CONFIG_HIBERNATION
s += sprintf(s, "%s\n", "disk");
#else
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '\n';
#endif
return (s - buf);
}
3.2.4 state_store 当系统 on的时候,才允许pm_suspend
点击(此处)折叠或打开
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state;
int error;
error = pm_autosleep_lock();
if (error)
return error;
if (pm_autosleep_state() > PM_SUSPEND_ON) {
error = -EBUSY;
goto out;
}
state = decode_state(buf, n);
if (state < PM_SUSPEND_MAX)
error = pm_suspend(state);
else if (state == PM_SUSPEND_MAX)
error = hibernate();
else
error = -EINVAL;
out:
pm_autosleep_unlock();
return error ? error : n;
}
3.3流程描述
3.3.1 PM初始化
点击(此处)折叠或打开
static int __init pm_init(void)
{
int error = pm_start_workqueue(); //
if (error)
return error;
hibernate_image_size_init();
hibernate_reserved_size_init();
touch_evt_timer_val = ktime_set(2, 0);
hrtimer_init(&tc_ev_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
tc_ev_timer.function = &tc_ev_stop;
tc_ev_processed = 1;
power_kobj = kobject_create_and_add("power", NULL);
if (!power_kobj)
return -ENOMEM;
error = sysfs_create_group(power_kobj, &attr_group); //创建sys文件接口
if (error)
return error;
return pm_autosleep_init(); //创建auto_sleep工作队列,也把用户态向autosleep 写入当作wakeup_source
3.3.2 Android手机起来后,会调用libsuspend.so。干的第一件事,就是向auto_sleep写入off
autosuspend_init->autosuspend_earlysuspend_init(会失败见3.2.4 state_store)->autosuspend_autosleep_init->autosuspend_autosleep_disable
对应内核就是
autosleep_store->pm_autosleep_set_state(PM_SUSPEND_ON)
点击(此处)折叠或打开
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); //__pm_stay_awake,保持唤醒状态注意stay,没有定时器处理超时
mutex_lock(&autosleep_lock); //临界资源保护,用户态在写入,try_to_suspend在检查autoslee_state
autosleep_state = state; //设置当前autosleep状态,就是消耗完唤醒事件
__pm_relax(autosleep_ws); //__pm_relax deactive唤醒事件
if (state > PM_SUSPEND_ON) { //
pm_wakep_autosleep_enabled(true);
queue_up_suspend_work(); //如果是第一次到这里,激活工作队列;如果是非第一次,调度工作项,其实try_to_suspend会一直循环处理
} else {
pm_wakep_autosleep_enabled(false); //对于PM_SUSPEND_ON,设置wakeup_source的autosleep_enabled为 flase
} //
mutex_unlock(&autosleep_lock);
return 0;
}
autosleep工作队列处理函数
点击(此处)折叠或打开
static void try_to_suspend(struct work_struct *work)
{
unsigned int initial_count, final_count;
if (!pm_get_wakeup_count(&initial_count, true)) //true会阻塞到这里,等待in process 为0。继续下面的检查,否则goto out
goto out;
mutex_lock(&autosleep_lock);
if (!pm_save_wakeup_count(initial_count)) { //保存wakeup_count,这里会再次判断in process 为0 。设置events_check_enabled为true。
mutex_unlock(&autosleep_lock); //events_check_enabled只是表明,当前wakeup事件没有发生,可以尝试pm_suspend
goto out;
}
if (autosleep_state == PM_SUSPEND_ON) { //ON是这一种情况,虽然进入了try_to_suspend,是否PM_SUSPEND_MEM造成的,但是状态又变了,应该完成这个工作项,不激活新的try_to_suspend
mutex_unlock(&autosleep_lock);
return;
}
if (autosleep_state >= PM_SUSPEND_MAX)
hibernate();
else
pm_suspend(autosleep_state); //冻结用户进程,执行设备的suspend,diable offline cpu。
mutex_unlock(&autosleep_lock);
if (!pm_get_wakeup_count(&final_count, false)) //resume后,获取wakeup_count
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) //系统被唤醒了,但是final_count没有增加,说明产生了未知的事件
schedule_timeout_uninterruptible(HZ / 2);
out:
queue_up_suspend_work();
}
4 debug描述
mount -t debugfs nodev /data/debug
cat /data/debug/wakeup_sources //观察wakeup_source情况
1.1 编写目的
分析Linux PM的框架及其具体实现
1.2 阅读建议
了解工作队列,等待队列,sys文件
1.3 参考资料
Linux 3.4
1.5 运行平台
Android 4.4
1.4 缩略语
PM:Power Management
2 总体描述
不管是嵌入式设备或者大型服务器,PM始终是Linux 必须关心的问题,因此一套成熟健壮的PM管理方案是必须的。目前大量手持设备都是基于Linux内核的,Android系统为了满足自身PM的需要,提出了wake_lock机制。但是这一直被Linux 主流所诟病,自从Linux3.4,负责Linux PM相关的Intel 员工 Rafael J. Wysocki <rjw@sisk.pl>在主流内核上面实现了基于wakeup_source的PM,并且兼容用户态继续使用wake_lock。
wakeup_source 定义的是唤醒事件,唤醒事件来源:内核产生,比如设备中断,rtc-alarm;用户态产生,避免自己在处理的时候,系统进入autosleep,用户进程被freeze。因此,autosleep就是一个优先级很低的任务了,只要一有wakeup_source产生,就不能进入睡眠或者已进入睡眠,就必须唤醒。Linux 实现周期性检测有内核定时器,工作队列。由于auto sleep优先级很低,目前采用工作队列机制。try_to_suspend这个工作项主要是读取目前的wakeup_count和in process count。如果in process count为0,说明现在很安静(很多唤醒事件都不产生了,这个概率其实很小),可以开始进入suspend。在进入suspend的时候,会保存wakeup_count,目的有两个:在进入suspend过程中,又有新的事件产生,就把每个事件的唤醒计数加1;在系统resume的时候,比较wake_count,如果保存事件和最新的事件计数不一致,说明产生了一个未知事件,让出cpu时间去处理这个未知事件。
用户态使用 echo mem > /sys/power/auto_sleep激活try_to_suspend或者,使能auto_sleep;由try_to_suspend检查当没有唤醒事件了,进入pm_suspend;当/sys/power/state为 on;echo mem > /sys/power/state,直接pm_suspend
3 设计描述
3.1 数据结构描述
struct wakeup_source {
const char *name;
struct list_head entry;
spinlock_t lock;
struct timer_list timer; //一些wakeup_source超时后,会变成deactive
unsigned long timer_expires; //超时时间
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; //产生事件计数
unsigned long active_count; //激活计数
unsigned long relax_count; //释放计数
unsigned long expire_count; //超时被变dactive计数
unsigned long wakeup_count; //当suspend后,唤醒系统计数
bool active:1;
bool autosleep_enabled:1; //事件是否允许系统autosleep
};
3.2 sys 接口描述
3.2.1 autosleep_show 显示autosleep状态 off表示不允许autosleep,mem表示允许
点击(此处)折叠或打开
static ssize_t autosleep_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
suspend_state_t state = pm_autosleep_state();
if (state == PM_SUSPEND_ON)
return sprintf(buf, "off\n");
#ifdef CONFIG_SUSPEND
if (state < PM_SUSPEND_MAX)
return sprintf(buf, "%s\n", valid_state(state) ?
pm_states[state] : "error");
#endif
#ifdef CONFIG_HIBERNATION
return sprintf(buf, "disk\n");
#else
return sprintf(buf, "error");
#endif
}
3.2.2 autosleep_store 设置
点击(此处)折叠或打开正常
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;
}
3.2.3 state_show打印出目前系统支持的autosleep状态
点击(此处)折叠或打开
tatic ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
char *s = buf;
#ifdef CONFIG_SUSPEND
int i;
for (i = 0; i < PM_SUSPEND_MAX; i++) {
if (pm_states[i] && valid_state(i))
s += sprintf(s,"%s ", pm_states[i]);
}
#endif
#ifdef CONFIG_HIBERNATION
s += sprintf(s, "%s\n", "disk");
#else
if (s != buf)
/* convert the last space to a newline */
*(s-1) = '\n';
#endif
return (s - buf);
}
3.2.4 state_store 当系统 on的时候,才允许pm_suspend
点击(此处)折叠或打开
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state;
int error;
error = pm_autosleep_lock();
if (error)
return error;
if (pm_autosleep_state() > PM_SUSPEND_ON) {
error = -EBUSY;
goto out;
}
state = decode_state(buf, n);
if (state < PM_SUSPEND_MAX)
error = pm_suspend(state);
else if (state == PM_SUSPEND_MAX)
error = hibernate();
else
error = -EINVAL;
out:
pm_autosleep_unlock();
return error ? error : n;
}
3.3流程描述
3.3.1 PM初始化
点击(此处)折叠或打开
static int __init pm_init(void)
{
int error = pm_start_workqueue(); //
if (error)
return error;
hibernate_image_size_init();
hibernate_reserved_size_init();
touch_evt_timer_val = ktime_set(2, 0);
hrtimer_init(&tc_ev_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
tc_ev_timer.function = &tc_ev_stop;
tc_ev_processed = 1;
power_kobj = kobject_create_and_add("power", NULL);
if (!power_kobj)
return -ENOMEM;
error = sysfs_create_group(power_kobj, &attr_group); //创建sys文件接口
if (error)
return error;
return pm_autosleep_init(); //创建auto_sleep工作队列,也把用户态向autosleep 写入当作wakeup_source
3.3.2 Android手机起来后,会调用libsuspend.so。干的第一件事,就是向auto_sleep写入off
autosuspend_init->autosuspend_earlysuspend_init(会失败见3.2.4 state_store)->autosuspend_autosleep_init->autosuspend_autosleep_disable
对应内核就是
autosleep_store->pm_autosleep_set_state(PM_SUSPEND_ON)
点击(此处)折叠或打开
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); //__pm_stay_awake,保持唤醒状态注意stay,没有定时器处理超时
mutex_lock(&autosleep_lock); //临界资源保护,用户态在写入,try_to_suspend在检查autoslee_state
autosleep_state = state; //设置当前autosleep状态,就是消耗完唤醒事件
__pm_relax(autosleep_ws); //__pm_relax deactive唤醒事件
if (state > PM_SUSPEND_ON) { //
pm_wakep_autosleep_enabled(true);
queue_up_suspend_work(); //如果是第一次到这里,激活工作队列;如果是非第一次,调度工作项,其实try_to_suspend会一直循环处理
} else {
pm_wakep_autosleep_enabled(false); //对于PM_SUSPEND_ON,设置wakeup_source的autosleep_enabled为 flase
} //
mutex_unlock(&autosleep_lock);
return 0;
}
autosleep工作队列处理函数
点击(此处)折叠或打开
static void try_to_suspend(struct work_struct *work)
{
unsigned int initial_count, final_count;
if (!pm_get_wakeup_count(&initial_count, true)) //true会阻塞到这里,等待in process 为0。继续下面的检查,否则goto out
goto out;
mutex_lock(&autosleep_lock);
if (!pm_save_wakeup_count(initial_count)) { //保存wakeup_count,这里会再次判断in process 为0 。设置events_check_enabled为true。
mutex_unlock(&autosleep_lock); //events_check_enabled只是表明,当前wakeup事件没有发生,可以尝试pm_suspend
goto out;
}
if (autosleep_state == PM_SUSPEND_ON) { //ON是这一种情况,虽然进入了try_to_suspend,是否PM_SUSPEND_MEM造成的,但是状态又变了,应该完成这个工作项,不激活新的try_to_suspend
mutex_unlock(&autosleep_lock);
return;
}
if (autosleep_state >= PM_SUSPEND_MAX)
hibernate();
else
pm_suspend(autosleep_state); //冻结用户进程,执行设备的suspend,diable offline cpu。
mutex_unlock(&autosleep_lock);
if (!pm_get_wakeup_count(&final_count, false)) //resume后,获取wakeup_count
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) //系统被唤醒了,但是final_count没有增加,说明产生了未知的事件
schedule_timeout_uninterruptible(HZ / 2);
out:
queue_up_suspend_work();
}
4 debug描述
mount -t debugfs nodev /data/debug
cat /data/debug/wakeup_sources //观察wakeup_source情况
0 0
- 基于wakeup_source的linux autosleep分析
- 基于wakeup_source的linux autosleep分析
- linux内核3.4基于wakeup_source的autosleep机制分析
- linux内核3.4基于wakeup_source的autosleep机制分析
- linux内核3.4基于wakeup_source的autosleep机制分析
- 基于wakeup_source的linux内核睡眠机制
- linux autosleep
- Linux wakelock与autosleep
- Linux电源管理-Autosleep
- [linux]pm autosleep flow
- earlysuspend、autosleep以及wakeup_count三种休眠机制的分析和比较
- earlysuspend、autosleep以及wakeup_count三种休眠机制的分析和比较
- Android休眠唤醒和wakeup_source机制的使用(1)
- Android休眠唤醒和wakeup_source机制的使用(2)
- Android休眠唤醒和wakeup_source机制的使用(1)
- 基于Arm 的linux 的启动分析
- 基于Arm 的linux 的启动分析
- 基于Arm 的linux 的启动分析
- 生成.X文件的一些方法&&DirectX获取及处理.X文件(初级版)
- Java所有关键字
- 线段树
- 目标关键词|搜索引擎优化工具|网站排名优化|网站排名|关键词分析
- Sketch使用教程(一)
- 基于wakeup_source的linux autosleep分析
- C语言--预处理指令、枚举、递归函数
- linux 查找目录或文件
- 安装系统20141223—2
- 网络聊天工具Socket编程心得
- java文档下载response的设置
- 告别系统传统Dialog弹出样式
- oracle 中序列问题的讨论
- 个性化控件收集