Android Kernel wake_lock analysis

本文主体内容是转载的,并修改了部分的内容。

1. Linux kernel wakelock 定义

A wake_lock prevents the system from entering suspend or other low power states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock prevents a full system suspend. If the type is WAKE_LOCK_IDLE, low powerstates that cause large interrupt latencies or that disable a set of interrupts will not entered from idle until the wake_locks are released.

当唤醒锁被激活的时候,它能阻止系统进入挂起状态或者其他低功耗模式。

如果锁类型被设置为 WAKE_LOCK_SUSPEND,该唤醒锁阻止整个系统进入挂起状态。

如果锁类型被设置为 WAKE_LOCK_IDLE,  除非该锁被释放,否则产生大量中断潜在因素或者禁止一系列中断的系统低功耗模式将被阻止进入。但是系统可以进入挂起睡眠状态。

 

2. 编译标志

根据Kconfig 文件, 相关的内核编译标志分别为:

CONFIG_HAS_WAKELOCK    选择是否使能 wakelock模块。

CONFIG_WAKELOCK:   依赖CONFIG_HAS_WAKELOCK。当用户空间请求睡眠状态时,该请求将被延置直到没有唤醒锁为使能状态。

CONFIG_WAKELOCK_STAT:      报告唤醒锁状态并写入到 /proc/wakelocks 文件

CONFIG_USER_WAKELOCK     用户空间唤醒锁。写入"锁名称或者超时锁名称” 到 /sys/power/wake_lock文件来使能唤醒锁 根据需要与否来创建一个唤醒锁)

                                                          写入锁名称到 /sys/power/wake_unlock 文件来解锁一个用户空间唤醒锁。

CONFIG_EARLYSUSPEND        当用户空间请求睡眠状态改变时, 调用early suspend handlers   该标志依赖于 CONFIG_WAKELOCK编译标志

实例:

void ath6kl_setup_android_resource(struct ath6kl *ar)

{

#ifdef CONFIG_HAS_EARLYSUSPEND   //用户空间请求睡眠状态的请求需要处理时

        ar->screen_off = false;

        ar->early_suspend.suspend = ath6kl_early_suspend;

        ar->early_suspend.resume = ath6kl_late_resume;

        ar->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;

        register_early_suspend(&ar->early_suspend);

#endif

#ifdef CONFIG_HAS_WAKELOCK   // 使能wakelock模块

wake_lock_init(&ar->wake_lock,

              WAKE_LOCK_SUSPEND,

              "ath6kl_lock_wl");

        wake_lock_init(&ar->p2p_wake_lock,

                      WAKE_LOCK_SUSPEND,

                       "ath6kl_p2p_suspend_wl");

#endif

}

wake_lock_init(&ar->wake_lock,

              WAKE_LOCK_SUSPEND,

              "ath6kl_lock_wl");

//初始化一个ar结构体中的唤醒锁,配置成WAKE_LOCK_SUSPEND类型,并且命名为“ath6kl_lock_wl”,可以通过cat /proc/wakelocks 查看。

睡眠状态的产生大致过程如下:

          用户空间请求睡眠

    ------->该请求等待到没有唤醒锁(wake_lock)处于激活(使能)状态才被处理

    ------->处理时调用early_suspend handlers

关于CONFIG_HAS_WAKELOCK的配置,在如下编译后产生的文件中可以看到:

/out/target/product/*****/obj/KERNEL_OBJ/include/config/auto.conf:167:CONFIG_HAS_WAKELOCK=y

./out/target/product/*****/obj/KERNEL_OBJ/include/generated/autoconf.h:169:#define CONFIG_HAS_WAKELOCK 1

./out/target/product/*****/obj/KERNEL_OBJ/.config:716:CONFIG_HAS_WAKELOCK=y

/out/target/product/****/obj/KERNEL_OBJ/.config:716:CONFIG_HAS_WAKELOCK=y

 

3. /proc/wakelocks 格式

示例如下

root@android:/ # cat /proc/wakelocks
cat /proc/wakelocks
name    count    expire_count    wake_count      active_since    total_time           sleep_time          max_time          last_change
"em"      4899               0                             0                   0          129695047374    123649908727  2367225591    29445791758668
"alarm_rtc"     2360    474     0       0       1613420206524   1611455462922   1949640000      27899269419825
"bcmpmu_i2c"    244500  0       1876    0       275160569412    268463363255    996054076       29445791728151
"KeyEvents"     11133   0       0       0       1853662043      601848424       231933595       29439508921751

 

4. 内核锁函数

初始化锁模块

1 static int __init wakelocks_init(void)  

2 {  

3     int ret;  

4     int i;  

5   

6         // 初始化suspend, idle 两个类型锁的链表   

7     for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)  

8         INIT_LIST_HEAD(&active_wake_locks[i]);  

9   

10 #ifdef CONFIG_WAKELOCK_STAT   

11     wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,  

12             "deleted_wake_locks");  

13 #endif   

        //初始化main锁并上锁。何处解main锁?early_suspend()   

14     wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND, "main");  

15     wake_lock(&main_wake_lock);  

16        //初始化 unknown_wakeups 锁   

17  wake_lock_init(&unknown_wakeup,WAKE_LOCK_SUSPEND,"unknown_wakeups");  

18        //初始化 suspend_backoff 锁   

19  //以上几个都是suspend锁。用户空间锁也是 suspend 类型。   

20     wake_lock_init(&suspend_backoff_lock, WAKE_LOCK_SUSPEND,  

21                "suspend_backoff");  

22   

23     ret = platform_device_register(&power_device);  

24     if (ret) {  

25         pr_err("wakelocks_init: platform_device_register failed\n");  

26         goto err_platform_device_register;  

27     }  

28     ret = platform_driver_register(&power_driver);  

29     if (ret) {  

30         pr_err("wakelocks_init: platform_driver_register failed\n");  

31         goto err_platform_driver_register;  

32     }  

33   

34     suspend_work_queue = create_singlethread_workqueue("suspend");  

35     if (suspend_work_queue == NULL) {  

36         ret = -ENOMEM;  

37         goto err_suspend_work_queue;  

38     }  

39   

40 #ifdef CONFIG_WAKELOCK_STAT   

41        //创建 /proc/wakelocks,  /proc/active_wakelocks 节点输出锁状态信息   

42     proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);  

43     proc_create("active_wakelocks", S_IRUGO, NULL, &active_wakelocks_fops);  

44 #endif   

45   

46     return 0;  

47   

48 err_suspend_work_queue:  

49     platform_driver_unregister(&power_driver);  

50 err_platform_driver_register:  

51     platform_device_unregister(&power_device);  

52 err_platform_device_register:  

53     wake_lock_destroy(&suspend_backoff_lock);  

54     wake_lock_destroy(&unknown_wakeup);  

55     wake_lock_destroy(&main_wake_lock);  

56 #ifdef CONFIG_WAKELOCK_STAT   

57     wake_lock_destroy(&deleted_wake_locks);  

58 #endif   

59     return ret;  

60 }  

初始化锁

61 void wake_lock_init(struct wake_lock *lock, int type, const char *name)  

wake_lock_init() 设置锁的名字,类型,最后将新建的锁挂接到一个专门链接这些非锁状态的链表inactive_locks上。 


上锁函数

62 void wake_lock(struct wake_lock *lock)  

63 {  

64     wake_lock_internal(lock, 0, 0);  

65 }  

66 EXPORT_SYMBOL(wake_lock);  

67   

68 void wake_lock_timeout(struct wake_lock *lock, long timeout)  

69 {  

70     wake_lock_internal(lock, timeout, 1);  

71 }  

 

wake_lock()和wake_lock_timeout() 分别对 inactive 的锁进行上锁。后者是超时锁。调用同一个内部函数 wake_lock_internal() 

72 static void wake_lock_internal(struct wake_lock *lock, long timeout, int has_timeout)  

73 {  

74     spin_lock_irqsave(&list_lock, irqflags);  

75     type = lock->flags & WAKE_LOCK_TYPE_MASK;  

76   

77        //上锁前, 检查锁类型和有效性   

78     BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);  

79     BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));  

80 #ifdef CONFIG_WAKELOCK_STAT   

81        //检查 wait_for_wakeup 标志   

82     if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {  

83         if (debug_mask & DEBUG_WAKEUP)  

84             pr_info("wakeup wake lock: %s\n", lock->name);  

85         wait_for_wakeup = 0;  

86         lock->stat.wakeup_count++;  

87     }  

88        //检查超时锁   

89     if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&  

90         (long)(lock->expires - jiffies) <= 0) {  

91         wake_unlock_stat_locked(lock, 0);  

92         lock->stat.last_time = ktime_get();  

93     }  

94 #endif   

95        //上锁   

96     if (!(lock->flags & WAKE_LOCK_ACTIVE)) {  

97         lock->flags |= WAKE_LOCK_ACTIVE;  

98 #ifdef CONFIG_WAKELOCK_STAT   

99         lock->stat.last_time = ktime_get();  

100 #endif   

101     }  

102        //先从链表中摘除该锁   

103     list_del(&lock->link);  

104     if (has_timeout) {  

105         if (debug_mask & DEBUG_WAKE_LOCK)  

106             pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",  

107                 lock->name, type, timeout / HZ,  

108                 (timeout % HZ) * MSEC_PER_SEC / HZ);  

109         lock->expires = jiffies + timeout;  

110         lock->flags |= WAKE_LOCK_AUTO_EXPIRE;  

111               //超时锁重新加入链表队尾   

112         list_add_tail(&lock->link, &active_wake_locks[type]);  

113     } else {  

114         if (debug_mask & DEBUG_WAKE_LOCK)  

115             pr_info("wake_lock: %s, type %d\n", lock->name, type);  

116         lock->expires = LONG_MAX;  

117         lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;  

118               //非超时锁直接加入队列   

119         list_add(&lock->link, &active_wake_locks[type]);  

120     }  

121     if (type == WAKE_LOCK_SUSPEND) {  

122         current_event_num++;  

123 #ifdef CONFIG_WAKELOCK_STAT   

124               //针对主锁的检查   

125         if (lock == &main_wake_lock)  

126             update_sleep_wait_stats_locked(1);  

127         else if (!wake_lock_active(&main_wake_lock))  

128             update_sleep_wait_stats_locked(0);  

129 #endif   

130         if (has_timeout)  

131                      //检查并遍历更新队列中所有超时锁。   

132             expire_in = has_wake_lock_locked(type);  

133         else  

134                      //非超时锁   

135             expire_in = -1;  

136         if (expire_in > 0) {  

137             if (debug_mask & DEBUG_EXPIRE)  

138                 pr_info("wake_lock: %s, start expire timer, "  

139                     "%ld\n", lock->name, expire_in);  

140             mod_timer(&expire_timer, jiffies + expire_in);  

141         } else {  

142                      //无任何超时锁   

143             if (del_timer(&expire_timer))  

144                 if (debug_mask & DEBUG_EXPIRE)  

145                     pr_info("wake_lock: %s, stop expire timer\n",  

146                         lock->name);  

147                      //如果超时锁为0, 且无非超时锁时, expire_in为0, 启动suspend流程   

148             if (expire_in == 0)  

149                 queue_work(suspend_work_queue, &suspend_work);  

150         }  

151     }  

152     spin_unlock_irqrestore(&list_lock, irqflags);  

153 }  

解锁函数

154 void wake_unlock(struct wake_lock *lock)  

155 {  

156     int type;  

157     unsigned long irqflags;  

158     spin_lock_irqsave(&list_lock, irqflags);  

159     type = lock->flags & WAKE_LOCK_TYPE_MASK;  

160 #ifdef CONFIG_WAKELOCK_STAT   

161     wake_unlock_stat_locked(lock, 0);  

162 #endif   

163     if (debug_mask & DEBUG_WAKE_LOCK)  

164         pr_info("wake_unlock: %s\n", lock->name);  

165        //去处锁标志   

166     lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);  

167        //该锁从 active列表移除, 加入inactive列表   

168     list_del(&lock->link);  

169     list_add(&lock->link, &inactive_locks);  

170     if (type == WAKE_LOCK_SUSPEND) {  

171               //检查是否需要启动suspend流程   

172         long has_lock = has_wake_lock_locked(type);  

173         if (has_lock > 0) {  

174             if (debug_mask & DEBUG_EXPIRE)  

175                 pr_info("wake_unlock: %s, start expire timer, "  

176                     "%ld\n", lock->name, has_lock);  

177             mod_timer(&expire_timer, jiffies + has_lock);  

178         } else {  

179             if (del_timer(&expire_timer))  

180                 if (debug_mask & DEBUG_EXPIRE)  

181                     pr_info("wake_unlock: %s, stop expire "  

182                         "timer\n", lock->name);  

183             if (has_lock == 0)  

184                 queue_work(suspend_work_queue, &suspend_work);  

185         }  

186         if (lock == &main_wake_lock) {  

187             if (debug_mask & DEBUG_SUSPEND)  

188                 print_active_locks(WAKE_LOCK_SUSPEND);  

189 #ifdef CONFIG_WAKELOCK_STAT   

190             update_sleep_wait_stats_locked(0);  

191 #endif   

192         }  

193     }  

194     spin_unlock_irqrestore(&list_lock, irqflags);  

195 }  


expire_wake_locks() 检查是否所有锁已经超时。 如果has_wake_lock_locked() 返回0,

针对suspend锁,必然是超时锁为0且非超时锁为0, 则启动suspend流程。

expire_wake_locks()是 expire_timer 的超时回调函数。

196 static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);  

5. suspend函数

197 static void suspend(struct work_struct *work)  

198 {  

199     int ret;  

200     int entry_event_num;  

201     struct timespec ts_entry, ts_exit;  

202   

203         //如果还有suspend锁,直接abort   

204     if (has_wake_lock(WAKE_LOCK_SUSPEND)) {  

205         if (debug_mask & DEBUG_SUSPEND)  

206             pr_info("suspend: abort suspend\n");  

207         return;  

208     }  

209   

210     entry_event_num = current_event_num;  

211        //内存同步   

212     sys_sync();  

213     if (debug_mask & DEBUG_SUSPEND)  

214         pr_info("suspend: enter suspend\n");  

215     getnstimeofday(&ts_entry);  

216        //系统suspend   

217        ret = pm_suspend(requested_suspend_state);  

218     getnstimeofday(&ts_exit);  

219   

220     if (debug_mask & DEBUG_EXIT_SUSPEND) {  

221         struct rtc_time tm;  

222         rtc_time_to_tm(ts_exit.tv_sec, &tm);  

223         pr_info("suspend: exit suspend, ret = %d "  

224             "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,  

225             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,  

226             tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);  

227     }  

228   

229     if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {  

230         ++suspend_short_count;  

231   

232         if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {  

233             suspend_backoff();  

234             suspend_short_count = 0;  

235         }  

236     } else {  

237         suspend_short_count = 0;  

238     }  

239   

240     if (current_event_num == entry_event_num) {  

241         if (debug_mask & DEBUG_SUSPEND)  

242             pr_info("suspend: pm_suspend returned with no event\n");  

243         wake_lock_timeout(&unknown_wakeup, HZ / 2);  

244     }  

245 }  

246 static DECLARE_WORK(suspend_work, suspend);  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值