wakelock申请流程:kernel/power/drivers/base/power/drivers/hisi/hi3xxx/hisi_lpregs.cdrivers/hisi/hi3xxx/pm.cframeworks/base/core/java/android/os/PowerManager.javaframeworks/base/services/core/java/com/android/server/power/PowerManagerService.javaframeworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpphardware/libhardware_legacy/power/power.cPowerManager.javapublic void acquire()public void acquire(long timeout)对外提供以上两个接口两个acquire都会调用到:acquireLockedmService.acquireWakeLock(mToken, mFlags, mTag, mPackageName, mWorkSource,                            mHistoryTag); //通过binder调用到acquireWakeLock@PowerManagerService.javaPowerManagerService.javaacquireWakeLock-->acquireWakeLockInternal-->updatePowerStateLocked-->updateSuspendBlockerLockedupdateSuspendBlockerLocked根据当前系统状态,判断需要调用mWakeLockSuspendBlocker.acquire()还是mWakeLockSuspendBlocker.release()在PowerManagerService的构造函数里:mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");    private SuspendBlocker createSuspendBlockerLocked(String name) {        SuspendBlocker suspendBlocker = new SuspendBlockerImpl(name);        mSuspendBlockers.add(suspendBlocker);        return suspendBlocker;    }private final class SuspendBlockerImpl implements SuspendBlocker {        @Override        public void acquire() {nativeAcquireSuspendBlocker(mName);//mName是在SuspendBlockerImpl构造函数里传入的,即createSuspendBlockerLocked时指定的名字}} nativeAcquireSuspendBlocker(mName); //com_android_server_power_PowerManagerService.cppacquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str()); // 如果按以上流程下来,这里的name就是"PowerManagerService.WakeLocks"acquire_wake_lock //hardware/libhardware_legacy/power/power.cwrite(fd, id, strlen(id))//fd是open("/sys/power/wake_lock", O_RDWR | O_CLOEXEC)返回的文件描述符wake_lock_store //kernel/power/main.cpm_wake_lock(buf);wakelock_lookup_add(buf, len, true);wakelock_lookup_add //kernel/power/wakelock.c先从wakelocks_tree里按名字查找要添加的wakelock是否已经存在,存在就return找到的wakelock,不存在再往下执行wakeup_source_add(&wl->ws)//内核的wakelock其实只做记录,记录系统中所有申请的wakelock,真正阻止休眠的是wakeup source框架rb_insert_color(&wl->node, &wakelocks_tree)//wakelocks_tree记录系统所有wakelockwakelocks_lru_add(wl)//用于wakelock回收class WakeLock implements IBinder.DeathRecipientPMS的WakeLock实现了DeathRecipient接口。根据Binder系统的知识可知,当Binder服务端死亡后,Binder系统会向注册了讣告接收的Binder客户端发送讣告通知,因此客户端可以做一些资源清理工作。在本例中,PM.WakeLock是Binder服务端,而PMS.WakeLock是Binder客户端。假如PM.WakeLock所在进程在release唤醒锁(即WakeLock)之前死亡,PMS.WakeLock的binderDied函数则会被调用,这样,PMS也能及时进行释放(release)工作。对于系统的重要资源来说,采用这种安全保护措施尤其必要。applogcat中的wakelock日志:PowerManagerService: acquireWakeLockInternal: lock=165206168, flags=0x1000001a, packageName=lte.trunk.terminal.tmophone, ws=null, uid=1001, pid=1667日志中flags=0x1000001a是指持有的wakelock类型,对于PARTIAL_WAKE_LOCK类型的wakelock不会打印日志public static final int FULL_WAKE_LOCK = 0x0000001a; //PowerManager.java--------------------------------------------------------------------------------wakelock日志周期修改:wakelock日志记录源码:vendor/huawei/chipset_common/modules/logs/sleeplogcat/sleeplogcat.c启动:service sleeplogcat /system/bin/sleeplogcat -t 2 -p /data/android_logs/sleeplog/ -f /system/etc/pwrlog.cfg      这行代码在文件vendor/huawei/extra/rc_files/init.platform.rc-t是时间,单位是秒wakelock是20s记录一次,这个时间太长,查问题有时候需要修改记录时间,比方改为1s。改动方法:sleeplogcat.c文件有一个函数:cat_node() {/*        int index;        for (index=0;index        }*/--> sleep(1);}g_timedelay在main初始化,是上面service命令传过来的值-t 2,即2s。--------------------------------------------------------------------------------wakelock日志解读与wakeup source阻止系统休眠的实现:nameactive_countevent_countwakeup_countexpire_countactive_sincetotal_timemax_timelast_changePowerManagerService.WakeLocks79798010155395915018871206PowerManagerService.Display77107089125028823871513Mains       11000001213USB         11000001213Battery     2728000346829790usb_wake_lock11100660630660630829790autosleep   0000000180active_count当一个wakeup event被处理且产生该wakeup event的wakeup source处于inactive状态时,计数增加event_count当一个wakeup event被处理时,计数增加wakeup_count在休眠最后一步suspend_enter函数被调用时,会通过pm_wakeup_pending函数检查系统中是否存在active wakeup source,如果有,那么suspend失败,计数增加expire_countpm_wakeup_event/wake_lock_timeout调用会设置一个timeout值,timeout时间到计数增加active_since在wakeup框架对应active_time,inactive时为0,active时计算方法为,每次打印日志时取当前时间t,然后减去last_time,这个差就是active_timetotal_time在active时更新,保存着active_time的累加值max_time在active时才有机会更新,每次打印日志时,判断active_time的时间,如果这次active_time比上次active_time大,那么更新max_time,如果小不更新last_change在wakeup框架对应last_time,active/inactive时会取当时时间,然后赋给last_time对active_count/event_count的说明:通俗讲,就是调用wakeup source激活函数时,wakeup source会变成active状态,然后active_count/event_count计数有增加的机会调用wakeup source吊销函数时wakeup source会变成inactive状态有时候会看到event_count的计数比active_count多,是因为比方调用了一次wake_lock,但还未调用wake_unlock,wakeup source还处于active状态,此时又调用了一次wake_lock,那么只增加event_count,active_count不变wakeup source激活函数:wake_lock/wake_lock_timeout/pm_wakeup_event/pm_stay_awakewakeup source吊销函数:wake_unlock/pm_relax这些函数在什么时候使用呢?wake_lock/wake_lock_timeout驱动中我们很常用pm_wakeup_event和wake_lock_timeout相同,都会设置timeout时间,pm_wakeup_event例子可参考gpio_keys驱动pm_stay_awake/__pm_stay_awake/__pm_wakeup_eventpower_supply/wifi驱动会调用pm_stay_awake用户空间申请wakelock最终会往sys/power/wake_lock写值,到内核空间是wake_lock_store@kernel/power/main.c,wake_lock_store调用了[email protected],pm_wake_lock又调用到了__pm_stay_awake/__pm_wakeup_eventstruct device {struct dev_pm_info  power;};struct dev_pm_info {struct wakeup_source    *wakeup;};从以上结构体可以看出,内核里的设备就是一个wakeup source,用户空间通过PowerManagerService获取wake lock,间接调用到内核wakeup框架,所以用户空间进程也可以是一个wakeup source,wakeup source产生一个wakeup event。“wakeup event被处理”具体做什么处理呢?其实就是更新wakeup相关的计数,具体是更新event_count,wakeup_count,active_count,last_time,combined_event_count这些变量wake_lock我们通常理解为持有一个锁,如果不想系统休眠那么调用该函数。其实从内核角度讲,看到的是计数。休眠时会判断计数,满足条件才休眠。那么休眠时是如何判断计数的呢?通过两个变量:原子变量combined_event_count无符号整型变量saved_countstatic atomic_t combined_event_count = ATOMIC_INIT(0);高16位保存着registered wakeup events,registered表示已经处理过的,当前处于inactive状态,所以registered wakeup events的意思是系统中阻止休眠的wakeup event总数,用低16位保存着wakeup events in progress,意思是当前正在处理的wakeup event。atomic_add_return(MAX_IN_PROGRESS, &combined_event_count)这行代码执行完,registered wakeup events会加1,wakeup events in progress会减1。atomic_add_return是原子操作,由arm汇编实现,简单可理解为combined_event_count = combined_event_count + MAX_IN_PROGRESS比如,combined_event_count = 0x00060001MAX_IN_PROGRESS 即 0x0000ffffMAX_IN_PROGRESS定义如下:#define IN_PROGRESS_BITS    (sizeof(int) * 4)#define MAX_IN_PROGRESS     ((1那么,combined_event_count = combined_event_count + MAX_IN_PROGRESS = 0x00060001 + 0x0000ffff = 0x00070000实现了高16位加1,低16位减1。combined_event_count值会在两个地方更新,wakeup_source_activate/wakeup_source_deactivate两个函数。当调用wakeup source激活函数时,会调用到wakeup_source_activate函数,在此函数里会对combined_event_count低16位加1,说明有wakeup event正在处理。当调用wakeup source吊销函数时,会调用到wakeup_source_deactivate函数,在此函数里会通过atomic_add_return对高16位加1,低16位减1。saved_count在pm_save_wakeup_count函数中更新,值为combined_event_count的高16位,表示系统中阻止休眠的wakeup event历史总数。在pm_wakeup_pending函数有两个判断条件,1. 取一次combined_event_count的高16位和saved_count比较,看是否相等2. 判断combined_event_count低16位是否为0两个条件都成立就可以睡眠,否则就不能睡眠。第一个条件满足表示从调用pm_save_wakeup_count开始到调用pm_wakeup_pending这段时间内,没有人调用wakeup source吊销函数;第二个条件满足表示没有人调用wakeup source激活函数。wakeup source吊销函数之前肯定会有wakeup source激活函数,这是成对出现的,wakeup source吊销函数不执行,说明也没有调激活函数,通俗讲就是在休眠最后时刻没有人用过wake lock。第二个条件说明没有wakeup event在处理。这里有个疑问,只判断第二个条件,即看有没有wakeup event处理不就行了吗,为什么要判断有没有人用过wake lock,有人用过,但现在已经释放了,还有影响吗?我的理解是,pm_wakeup_pending在suspend_enter里调用,是休眠流程中最后一项检查,也就是在此之前freeze_processes/device_suspend已经执行过,说明所有设备已经进入到休眠态,process也已经在冻结态。如果第一个条件不成立,说明有人用wake lock了,可以说有device或process醒来过,虽然现在wake lock已经释放,但醒来就说不好可能还有其他未完成的事情,这时候就不适合继续休眠下去。--------------------------------------------------------------------------------android休眠流程:android提供了三种休眠路径,earlysuspend、autosleep、wakeup_count。earlysuspend:kernel里没有提供对应的sys结点,应该是废弃掉了。autosleep:在适当时机,直接写sys文件触发休眠,echo mem > /sys/power/autosleep。wakeup_count:结合kernel的wakeup count,在适当时机echo mem > /sys/power/state触发休眠。适当时机是由PowerManagerService决定:setHalAutoSuspendModeLocked@frameworks/base/services/core/java/com/android/server/power/PowerManagerService.javanativeSetAutoSuspend@frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cppautosuspend_enable@system/core/libsuspend/autosuspend.cautosuspend_ops->enable()如果注册的是wakeup_count,那么上述会调用到autosuspend_wakeup_count_enable,wakeup_count休眠路径是这样做的:suspend_thread_func@system/core/libsuspend/autosuspend_wakeup_count.c {while(1) {msleep(100);read(wakeup_count_fd, wakeup_count,sizeof(wakeup_count)); //wakeup_count_show-->[email protected]如果有正在处理的wakeup event,那么调用schedule休眠,否则返回历史wakeup count总数sem_wait(&suspend_lockout);//等待合适的休眠时机,autosuspend_wakeup_count_enable函数会调用sem_post(&suspend_lockout);write(wakeup_count_fd, wakeup_count, wakeup_count_len);//wakeup_count_store-->[email protected]其实并没写东西到什么地方,只是判断传入的count值和//当前从combined_event_count取出的count值是否相等,相等表示从读count到写count这段时间没有新的wakeup event产生,返回正值,否则返回负值write(state_fd, sleep_state, strlen(sleep_state));//[email protected] mem > /sys/power/state如果上面写count的write调用返回正值,那么调用此write,触发系统休眠}}echo mem > /sys/power/state在kernel里流程是:state_store@kernel/power/main.cstate = decode_state(buf, n)//解析出来是这三个值 "freeze", "standby", "mem"pm_suspend(state)@kernel/power/suspend.c//这个函数会打印PM: suspend entry 2016-12-19 08:31:07.846252600 UTC如果注册的是autosleep,那么会调用到autosuspend_autosleep_enable,这个函数就直接echo mem > /sys/power/autosleep,kernel里的流程是:autosleep_store-->pm_autosleep_set_state-->queue_up_suspend_work-->try_to_suspendtry_to_suspendpm_save_wakeup_countpm_suspend两种都会调用到pm_suspendpm_suspendenter_statesys_sync//将缓存中的数据写入块设备suspend_preparepm_prepare_console//将当前console切换到一个虚拟console并重定向内核的kmsgpm_notifier_call_chain(PM_SUSPEND_PREPARE)suspend_freeze_processesfreeze_processes__usermodehelper_disable(UMH_FREEZING)try_to_freeze_tasks(true)freeze_taskfreeze_kernel_threadstry_to_freeze_tasks(false)freeze_tasksuspend_devices_and_entersuspend_ops->begin(state)suspend_consoleftrace_stopdpm_suspend_startdpm_preparedevice_preparedpm_suspendcpufreq_suspend__cpufreq_governordevice_suspend__device_suspendsuspend_enterdpm_suspend_enddisable_nonboot_cpusarch_suspend_disable_irqssyscore_suspendpm_wakeup_pendingsuspend_ops->[email protected]作用:/* * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source * is active, otherwise a deadlock with try_to_suspend() is possible. * Alternatively mutex_lock_interruptible() can be used.  This will then fail * if an auto_sleep cycle tries to freeze processes. */就是说,mutex_lock(&autosleep_lock)的调用,必须在有active状态的wakeup_source才安全,否则在 try_to_suspend()时可能发生死锁,我的理解是: 例如,pm_autosleep_set_state中,需要更改autosleep_state(一个全局变量),因而需要使用mutex_lock保护临界区。如果调用时序如下: 1. 系统具备suspend的条件,调用try_to_suspend试图suspend系统,该接口调用mutex_lock(&autosleep_lock);,持有锁。 2. 发生调度,执行pm_autosleep_set_state所在的进程,在该接口中调用mutex_lock(&autosleep_lock)。因为此时锁已经被try_to_suspend占用,进程sleep,且状态为TASK_UNINTERRUPTIBLE(这个状态会导致task freezing失败)。 3. 发生调度,继续执行try_to_suspend,直到执行freeze操作(suspend_freeze_processes)。由于有进程处于TASK_UNINTERRUPTIBLE状态,freeze失败,suspend失败。 因此,出现了“具备suspend条件,却suspend失败了”,就是上面注释中所说的deadlock问题。所以在mutex_lock(&autosleep_lock)调用前,保持系统不会suspend,就可以解决这种问题。
10-08 07:07