static int __init pm_init(void) { int error = pm_start_workqueue(); if (error) return error; hibernate_image_size_init(); hibernate_reserved_size_init(); power_kobj = kobject_create_and_add("power", NULL); if (!power_kobj) return -ENOMEM; #ifdef CONFIG_X86_INTEL_XGOLD power_hal_kobj = kobject_create_and_add("power_HAL_suspend", power_kobj); if (!power_hal_kobj) return -ENOMEM; #endif error = sysfs_create_group(power_kobj, &attr_group); if (error) return error; pm_print_times_init(); return pm_autosleep_init(); } core_initcall(pm_init);
pm_init使用core_initcall,顾名思义这是内核一个核心节点。pm_init完成创建"power"节点,至于如何创建,节点位置,想了解的看下如下这一小部分
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent) { struct kobject *kobj; int retval; kobj = kobject_create(); if (!kobj) return NULL; retval = kobject_add(kobj, parent, "%s", name); if (retval) { printk(KERN_WARNING "%s: kobject_add error: %d\n", __func__, retval); kobject_put(kobj); kobj = NULL; } return kobj; } EXPORT_SYMBOL_GPL(kobject_create_and_add);
struct kobject *kobject_create(void) { struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL); if (!kobj) return NULL; kobject_init(kobj, &dynamic_kobj_ktype); return kobj; }
static struct kobj_type dynamic_kobj_ktype = { .release = dynamic_kobj_release, .sysfs_ops = &kobj_sysfs_ops, };
power作为内核待机主要节点,也可以说是父节点,很多相关节点都在它低下创建;这就要看它的attribute_group
static struct attribute * g[] = { &state_attr.attr, #ifdef CONFIG_PM_TRACE &pm_trace_attr.attr, &pm_trace_dev_match_attr.attr, #endif #ifdef CONFIG_PM_SLEEP &pm_async_attr.attr, &wakeup_count_attr.attr, #ifdef CONFIG_PM_AUTOSLEEP &autosleep_attr.attr, #endif #ifdef CONFIG_PM_WAKELOCKS &wake_lock_attr.attr, &wake_unlock_attr.attr, #endif #ifdef CONFIG_PM_DEBUG &pm_test_attr.attr, #endif #ifdef CONFIG_PM_SLEEP_DEBUG &pm_print_times_attr.attr, #endif #endif #ifdef CONFIG_FREEZER &pm_freeze_timeout_attr.attr, #endif NULL, };
如下是创建的节点群
# ls /sys/power/
从之前的文章分析,内核还在3.10之前,state节点是待机唯一入口。
当然内核延续性,这些接口作用都不变也可以用,如下命令待机:
# cat /sys/power/state
freeze mem
# echo mem > sys/power/state
[ 168.644948] PM: suspend entry 2015-06-12 06:48:07.903751731 UTC
[ 168.651206] PM: Syncing filesystems ... done.
[ 168.704213] Freezing user space processes ... (elapsed 0.005 seconds) done.
[ 168.717090] Freezing remaining freezable tasks ... (elapsed 0.004 seconds) done.
[ 168.728986] Suspending console(s) (use no_console_suspend to debug)
从上面子节点群,我们注意到增加了一个叫autosleep的节点。没错!这是新增的接口,autosleep替代以前google加在linux之上的earlysuspend机制,所以从这个版本开始,不需要google的android来操心内核增加什么待机方面代码了。
我们先看这个入口函数的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; }
接下来就进入到autosleep的主要回调pm_autosleep_set_state去了。
内核3.10用的加锁解锁流程是
wake_lock_init初始化一个lock
wake_lock加锁(非超时锁,需要手动解锁)
wake_lock_timeout加超时锁,到时自动解锁
wake_unlock解锁
wake_lock_destroy销毁一个lock
内核3.14兼容这些接口,开发者可以使用之前的驱动代码,也可以保持风格,因为这些接口都做了兼容,
在include/linux/wakelock.h里面都有兼容性定义;如
static inline void wake_lock_init(struct wake_lock *lock, int type, const char *name) { wakeup_source_init(&lock->ws, name); }
内核3.14使用wakelock source概念,通过锁的红黑树记录并管理系统所有的锁,这是锁机制的最大改变
static struct rb_root wakelocks_tree = RB_ROOT;
我们后面单独介绍,现在先把使用流程搞清楚。
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; }进入到autosleep的回调后,先加上一些锁,保证状态机的切换完成。然后激活待机工作队列执行suspend_work
try_to_suspend是autosleep最核心的部分,也是替换旧版内核使用timer结束后轮询是否还有timer来实现不停尝试待机。
开函数名字就知道,它会一直尝试待机,这就要求它能够及时响应,当系统最后一个锁被释放,它要能及时响应进入待机。
我们先看下它的函数实现
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; 检查wakeup count,注意所带参数true表示不只是get,而是有可能会停留在里面 mutex_lock(&autosleep_lock); if (!pm_save_wakeup_count(initial_count) || system_state != SYSTEM_RUNNING) { mutex_unlock(&autosleep_lock); goto out; } 保存当前的wakeup事件count,用于对wakeup事件count的统计,进一步是为了避免过多特别快的wakeup事件 if (autosleep_state == PM_SUSPEND_ON) { mutex_unlock(&autosleep_lock); return; } if (autosleep_state >= PM_SUSPEND_MAX) hibernate(); else pm_suspend(autosleep_state); 满足待机条件,进入待机如后函数pm_suspend,这个以前是在state_store调用的 mutex_unlock(&autosleep_lock); if (!pm_get_wakeup_count(&final_count, false)) goto out; 检查wakeup count,带参数false表示只是get下count,不会block在里面,确切说只是更新下count table /* * 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); 如果唤醒过快(前面提到了),就等待0.5秒 out: queue_up_suspend_work(); 激活自己,重新开始工作队列,这也是try-的意思 }
bool pm_get_wakeup_count(unsigned int *count, bool block) { unsigned int cnt, inpr; if (block) { 如果参数是true,就会进入这个case,有可能会block在这里面 DEFINE_WAIT(wait); for (;;) { prepare_to_wait(&wakeup_count_wait_queue, &wait, TASK_INTERRUPTIBLE); prepare好wait的条件 split_counters(&cnt, &inpr); if (inpr == 0 || signal_pending(current)) break; schedule();等待 } finish_wait(&wakeup_count_wait_queue, &wait);结束等待,被interrupt了 } split_counters(&cnt, &inpr); 更新lock table,如果是参数false,就直接过来到这里,所以只会更新table而不会block *count = cnt; return !inpr; }
1. 使用兼容的接口
wake_lock_init初始化一个lock
wake_lock加锁(非超时锁,需要手动解锁)
wake_lock_timeout加超时锁,到时自动解锁
wake_unlock解锁
wake_lock_destroy销毁一个lock
定义一个struct wakeup_source
wakeup_source_register("wakelockname");注册这个wakeup_source
__pm_stay_awake(wakelockname);加锁(如果需要超时锁,在ws里面赋值timer_expires)
__pm_relax(wakelockname)
wakeup_source_unregister(wakelockname);撤销ws