1 引言
1.1 编写目的
分析Linux PM的框架及其具体实现
1.2 阅读建议
了解工作队列,等待队列,sys文件
1.3 参考资料
Linux 3.4
1.5 运行平台
Android 4.4
1.4 缩略语
PM:Power Management
2 总体描述
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.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
- }
点击(此处)折叠或打开正常
-
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;
- }
点击(此处)折叠或打开
-
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);
- }
点击(此处)折叠或打开
-
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;
- }
点击(此处)折叠或打开
-
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情况