核心内容摘要
ZZJJ日本音乐之旅开启:一场穿越古今的旋律盛宴
目录简介
wakelocks
Kernel wakelocks在电源管理中的位置
wakelocks 内核源码分析
创建 /sys/power/wake_lock 和 /sys/power/wake_unlock
pm_wake_lock() 接口
pm_wake_unlock() 接口
__wakelocks_gc()回收处理work
工作时序简介Wakelocks 框架是基于Wakeup Source实现的为Android系统上层提供投票机制以阻止系统进入休眠。
该模块的支持受宏CONFIG_PM_WAKELOCKS控制。
在使能该宏的情况下PM Core初始化过程中会在sysfs下创建两个属性节点/sys/power/wake_lock:用户程序可以向其写入一个字符串来创建一个wakelock该字符创即为wakelock的名字该wakelock可阻止系统进入低功耗模式/sys/power/wake_unlock:用户程序向其写入相同的字符串即可注销该wakelock配置宏CONFIG_PM_WAKELOCKS_LIMIT可以限制系统所能创建的wakelock的数量。
使能宏CONFIG_PM_WAKELOCKS_GC能打开wakelock的回收机制使得wakelock在积累一定的数量后再去清除释放空间从而不需要在每次释放wakelock时都去清除。
wakelockswakelocks提供的功能包括1/sys/power/wake_lock用户程序向文件写入一个字符串即可创建一个wakelock该字符串就是wakelock的名字。
该wakelock可以阻止系统进入低功耗模式2/sys/power/wake_unlock用户程序向文件写入相同的字符串即可注销一个wakelock3当系统中所有的wakelock都注销后系统可以自动进入低功耗状态由autosleep实现4向内核其它driver也提供了wakelock的创建和注销接口允许driver创建wakelock以阻止睡眠、注销wakelock以允许睡眠。
并允许用户空间访问有关Android wakelocks更为详细的描述可以参考http://elinux.org/Android_Power_Management
Kernel wakelocks在电源管理中的位置在PM core中有一个wakelock模块kernel/power/wakelock.c该模块依赖wakeup events framework提供的wakeup source机制实现用户空间的wakeup source就是wakelocks并通过PM core main模块向用户空间提供两个同名的sysfs文件wake_lock和wake_unlock。
wakelocks 内核源码分析该模块的支持受宏CONFIG_PM_WAKELOCKS控制。
在使能该宏的情况下PM Core初始化过程中会在sysfs下创建两个属性节点/sys/power/wake_lock用户程序可以向其写入一个字符串来创建一个wakelock该字符创即为wakelock的名字该wakelock可阻止系统进入低功耗模式/sys/power/wake_unlock用户程序向其写入相同的字符串即可注销该wakelock
创建 /sys/power/wake_lock 和 /sys/power/wake_unlockpm_init() 调用 kobject_create_and_add 和 sysfs_create_groups 函数创建一系列的属性文件其中 wake_lock_attr、wake_unlock_attr 分别会生成 /sys/power/wake_lock 和 /sys/power/wake_unlock/* kernel/power/main.c */ 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_SUSPEND mem_sleep_attr.attr, sync_on_suspend_attr.attr, #endif #ifdef CONFIG_PM_AUTOSLEEP autosleep_attr.attr, #endif #ifdef CONFIG_PM_WAKELOCKS wake_lock_attr.attr, //wake_lock wake_unlock_attr.attr, //wake_unlock #endif #ifdef CONFIG_PM_SLEEP_DEBUG pm_test_attr.attr, pm_print_times_attr.attr, pm_wakeup_irq_attr.attr, pm_debug_messages_attr.attr, #endif #endif #ifdef CONFIG_FREEZER pm_freeze_timeout_attr.attr, #endif NULL, }; static const struct attribute_group attr_group { .attrs g, }; static const struct attribute_group *attr_groups[] { attr_group, #ifdef CONFIG_PM_SLEEP suspend_attr_group, #endif NULL, }; struct workqueue_struct *pm_wq; EXPORT_SYMBOL_GPL(pm_wq); static int __init pm_start_workqueue(void) { pm_wq alloc_workqueue(pm, WQ_FREEZABLE,
; return pm_wq ? 0 : -ENOMEM; } static int __init pm_init(void) { int error pm_start_workqueue(); if (error) return error; hibernate_image_size_init(); hibernate_reserved_size_init(); pm_states_init(); power_kobj kobject_create_and_add(power, NULL); if (!power_kobj) return -ENOMEM; error sysfs_create_groups(power_kobj, attr_groups); if (error) return error; pm_print_times_init(); return pm_autosleep_init(); } core_initcall(pm_init);wake_lock_attr 和 wake_unlock_attr 的 .show、.store 定义如下#define power_attr(_name) \ static struct kobj_attribute _name##_attr { \ .attr { \ .name __stringify(_name), \ .mode 0644, \ }, \ .show _name##_show, \ .store _name##_store, \ } #ifdef CONFIG_PM_WAKELOCKS static ssize_t wake_lock_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return pm_show_wakelocks(buf, true); } static ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { int error pm_wake_lock(buf); return error ? error : n; } power_attr(wake_lock); static ssize_t wake_unlock_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { return pm_show_wakelocks(buf, false); } static ssize_t wake_unlock_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t n) { int error pm_wake_unlock(buf); return error ? error : n; } power_attr(wake_unlock); #endif /* CONFIG_PM_WAKELOCKS */用户空间往/sys/power/wake_lock写数据最终调用 pm_wake_lock用户空间往/sys/power/wake_unlock写数据最终调用 pm_wake_unlock
pm_wake_lock() 接口向/sys/power/wake_lock写如字符串时调用pm_wake_lock()pm_wake_lock()主要实现功能查找同名 wakelock找不到时创建 wakelock并持超时锁配置CONFIG_PM_WAKELOCKS_LIMIT 0的情况下对wakelock数量计数并限制将该wakelock移到回收链表前端以防被优先回收/* kernel/power/wakelock.c*/ int pm_wake_lock(const char *buf) { const char *str buf; struct wakelock *wl; u64 timeout_ns 0; size_t len; int ret 0; if (!capable(CAP_BLOCK_SUSPEND)) return -EPERM; //解析传入的字符串第一个参数为wakelock名称第二个参数可选则是wakelock超时时间 while (*str !isspace(*str)) str; len str - buf; if (!len) return -EINVAL; if (*str *str ! \n) { /* Find out if theres a valid timeout string appended. */ ret kstrtou64(skip_spaces(str), 10, timeout_ns); if (ret) return -EINVAL; } mutex_lock(wakelocks_lock); //查找wakelock找不到时创建 wl wakelock_lookup_add(buf, len, true); if (IS_ERR(wl)) { ret PTR_ERR(wl); goto out; } if (timeout_ns) { //如果传入了超时参数则持锁超时后会自动释放该锁 u64 timeout_ms timeout_ns NSEC_PER_MSEC - 1; do_div(timeout_ms, NSEC_PER_MSEC); __pm_wakeup_event(wl-ws, timeout_ms); } else { //否则直接持锁 __pm_stay_awake(wl-ws); } wakelocks_lru_most_recent(wl); //将该wakelock移到回收链表前端使得回收机制触发时靠后处理 out: mutex_unlock(wakelocks_lock); return ret; }wakelock_lookup_add() 查找wakelock找不到时创建static struct wakelock *wakelock_lookup_add(const char *name, size_t len, bool add_if_not_found) { struct rb_node **node wakelocks_tree.rb_node; struct rb_node *parent *node; struct wakelock *wl; //根据名称在红黑树上查找是否已经存在该wakelock while (*node) { int diff; parent *node; wl rb_entry(*node, struct wakelock, node); diff strncmp(name, wl-name, len); if (diff
{ if (wl-name[len]) diff -1; else return wl; //找到同名wakelock返回 } if (diff
node (*node)-rb_left; else node (*node)-rb_right; } if (!add_if_not_found) return ERR_PTR(-EINVAL); //配置CONFIG_PM_WAKELOCKS_LIMIT0的情况下会检测已创建的wakelock数量是否已经超过该配置 if (wakelocks_limit_exceeded()) return ERR_PTR(-ENOSPC); //未找到同名wakelock的情况下开始创建wakelock /* Not found, we have to add a new one. */ wl kzalloc(sizeof(*wl), GFP_KERNEL); if (!wl) return ERR_PTR(-ENOMEM); wl-name kstrndup(name, len, GFP_KERNEL); if (!wl-name) { kfree(wl); return ERR_PTR(-ENOMEM); } //本质wakelock是通过wakeup_source机制实现的 wl-ws wakeup_source_register(NULL, wl-name); if (!wl-ws) { kfree(wl-name); kfree(wl); return ERR_PTR(-ENOMEM); } wl-ws-last_time ktime_get(); //将该wakelock挂到红黑树上 rb_link_node(wl-node, parent, node); rb_insert_color(wl-node, wakelocks_tree); wakelocks_lru_add(wl); //添加到回收链表 increment_wakelocks_number(); //wakelock数量1 return wl; }
pm_wake_unlock()接口向/sys/power/wake_unlock写入字符串时调用pm_wake_unlock()查找同名wakelock找不到时返回错误释放锁配置CONFIG_PM_WAKELOCKS_GC开启回收机制的情况下对wakelock数量计数并在超过上限时触发回收处理work/* kernel/power/wakelock.c */ int pm_wake_unlock(const char *buf) { struct wakelock *wl; size_t len; int ret 0; if (!capable(CAP_BLOCK_SUSPEND)) return -EPERM; len strlen(buf); if (!len) return -EINVAL; if (buf[len-1] \n) len--; if (!len) return -EINVAL; mutex_lock(wakelocks_lock); //查找wakelock找不到时直接返回错误 wl wakelock_lookup_add(buf, len, false); if (IS_ERR(wl)) { ret PTR_ERR(wl); goto out; } __pm_relax(wl-ws); //释放锁 wakelocks_lru_most_recent(wl); //将该wakelock移到回收链表前端使得回收机制触发时靠后处理 wakelocks_gc(); //已解锁的wakelock加1并判断是否超过上限触发回收处理work out: mutex_unlock(wakelocks_lock); return ret; }
__wakelocks_gc()回收处理work该接口在已解锁的wakelock数量超过上限 WL_GC_COUNT_MAX(
时调用用于处理回收已创建的wakelock释放空间。
static void __wakelocks_gc(struct work_struct *work) { struct wakelock *wl, *aux; ktime_t now; mutex_lock(wakelocks_lock); now ktime_get(); //从回收链表尾部开始倒序遍历越靠近链表头部的wakelock越是最近才操作的wakelock list_for_each_entry_safe_reverse(wl, aux, wakelocks_lru_list, lru) { u64 idle_time_ns; bool active; spin_lock_irq(wl-ws-lock); idle_time_ns ktime_to_ns(ktime_sub(now, wl-ws-last_time)); //计算该锁有多长时间未被操作过 active wl-ws-active; //获取锁的激活状态 spin_unlock_irq(wl-ws-lock); if (idle_time_ns ((u