Chinaunix首页 | 论坛 | 博客
  • 博客访问: 179666
  • 博文数量: 27
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 618
  • 用 户 组: 普通用户
  • 注册时间: 2013-11-15 09:12
文章分类
文章存档

2014年(17)

2013年(10)

我的朋友

分类: Android平台

2014-01-26 17:43:00

从这篇开始我就开始对android下的wakelock和suspend是如何结合做些自己的分析,长话短说吧,我们首先看看个函数:

路径如下:

kernel/kernel/power/main.c

这个函数就是Native曾调用的接口程序,比如:state, wakelock , unwakelock,这里都有具体实现,下面我们先分析下wake_lock相关函数,上锁过程:
(注意我只分析store函数 show函数我就不分析了)

456 static ssize_t wake_lock_store(struct kobject *kobj,
457                                struct kobj_attribute *attr,
458                                const char *buf, size_t n)
459 {
460         int error = pm_wake_lock(buf);
461         return error ? error : n;
462 }

这端函数没有什么可说的,就是直接把上层申请的锁名称传到pm_wake_lock中,比如 mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService"); 中的PowerManagerService。我们继续看下pm_wake_lock函数,
183 int pm_wake_lock(const char *buf)
184 {
185         const char *str = buf;
186         struct wakelock *wl;
187         u64 timeout_ns = 0;
188         size_t len;
189         int ret = 0;
190
191         while (*str && !isspace(*str))
192                 str++;
193
194         len = str - buf;
195         if (!len)
196                 return -EINVAL;
197
198         if (*str && *str != '\n') {
199                 /* Find out if there's a valid timeout string appended. */
200                 ret = kstrtou64(skip_spaces(str), 10, &timeout_ns);
201                 if (ret)
202                         return -EINVAL;
203         }
204
205         mutex_lock(&wakelocks_lock);
206
207         wl = wakelock_lookup_add(buf, len, true);
208         if (IS_ERR(wl)) {
209                 ret = PTR_ERR(wl);
210                 goto out;
211         }
212         if (timeout_ns) {
213                 u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
214
215                 do_div(timeout_ms, NSEC_PER_MSEC);
216                 __pm_wakeup_event(&wl->ws, timeout_ms);
217         } else {                                                                       
218                 __pm_stay_awake(&wl->ws);
219         }
220
221         wakelocks_lru_most_recent(wl);
222
223  out:
224         mutex_unlock(&wakelocks_lock);
225         return ret;
226 }

这端函数主要功能在于检查这个wakelock是否存在红黑二叉树上,把名字短的放在左叉,名字长的放在右叉,这里是不是跟进程调度的二叉树很像?^_^,然后调用__pm_wakeup_event或者__pm_stay_awake函数,这里的timeout_ms我没有看明白怎么取得的,烦人,以后慢慢看看再。继续跟踪没有timeout_ms的函数吧,另外的函数进入也干了同样的事情,还对timeout进行了一些处理,可能和两种锁有关系哦?一种是带timeout时间的锁,到时会注销,一种是不带时间锁,如果没有显示的unlock就不会放弃锁。
418 void __pm_stay_awake(struct wakeup_source *ws)
419 {
420         unsigned long flags;
421
422         if (!ws)
423                 return;
424
425         spin_lock_irqsave(&ws->lock, flags);
426
427         wakeup_source_report_event(ws);
428         del_timer(&ws->timer);
429         ws->timer_expires = 0;
430
431         spin_unlock_irqrestore(&ws->lock, flags);
432 } 

这个函数主要干的就是调用wakeup_source_report_evet这个函数

01 static void wakeup_source_report_event(struct wakeup_source *ws)
402 {
403         ws->event_count++;
404         /* This is racy, but the counter is approximate anyway. */
405         if (events_check_enabled)
406                 ws->wakeup_count++;
407         
408         if (!ws->active)
409                 wakeup_source_activate(ws);

381 static void wakeup_source_activate(struct wakeup_source *ws)
382 {
383         unsigned int cec;
384
385         ws->active = true;
386         ws->active_count++;
387         ws->last_time = ktime_get();
388         if (ws->autosleep_enabled)
389                 ws->start_prevent_time = ws->last_time;
390
391         /* Increment the counter of events in progress. */
392         cec = atomic_inc_return(&combined_event_count);                                                                                    
393
394         trace_wakeup_source_activate(ws->name, cec);
395 }

这两段函数连续看,会发现,对与一些count进行的++,设置了下wake_lock中的wake_source的一些变量,主要是 对于combined_event_count这个变量进行的原子操作++,(这个变量暂时理解胃是记录有多少个wakelock吧),然后就完活了啊,这样就对一个wakelock进行。

我们再分析下wake_unlock_store这个函数,

473 static ssize_t wake_unlock_store(struct kobject *kobj,
474                                  struct kobj_attribute *attr,
475                                  const char *buf, size_t n)
476 {
477         int error = pm_wake_unlock(buf);
478         return error ? error : n;
479 }

操作一样,把写到节点的字符串传到pm_wake_unlock(buf)函数中
228 int pm_wake_unlock(const char *buf)
229 {
230         struct wakelock *wl;
231         size_t len;
232         int ret = 0;
233
234         len = strlen(buf);
235         if (!len)
236                 return -EINVAL;
237
238         if (buf[len-1] == '\n')
239                 len--;
240
241         if (!len)
242                 return -EINVAL;
243
244         mutex_lock(&wakelocks_lock);
245
246         wl = wakelock_lookup_add(buf, len, false);
247         if (IS_ERR(wl)) {
248                 ret = PTR_ERR(wl);
249                 goto out;
250         }
251         __pm_relax(&wl->ws);
252
253         wakelocks_lru_most_recent(wl);
254         wakelocks_gc();
255
256  out:
257         mutex_unlock(&wakelocks_lock);
258         return ret;
259 }

这里也是在wake_lock二叉树查找wake_lock,然后调用__pm_relax(&wl->ws);这个函数,精髓都在这里,我们跟踪下去
535 void __pm_relax(struct wakeup_source *ws)
536 {
537         unsigned long flags;
538
539         if (!ws)
540                 return;
541
542         spin_lock_irqsave(&ws->lock, flags);
543         if (ws->active)
544                 wakeup_source_deactivate(ws);
545         spin_unlock_irqrestore(&ws->lock, flags);
546 }
547 EXPORT_SYMBOL_GPL(__pm_relax);

看到ws->active了吗?这个在之前上锁的时候我们定义过true哦,然后调用wakeup_source_deactivate这个函数,

478 static void wakeup_source_deactivate(struct wakeup_source *ws)
479 {
480         unsigned int cnt, inpr, cec;
481         ktime_t duration;
482         ktime_t now;
483
484         ws->relax_count++;
485         /*
486          * __pm_relax() may be called directly or from a timer function.
487          * If it is called directly right after the timer function has been
488          * started, but before the timer function calls __pm_relax(), it is
489          * possible that __pm_stay_awake() will be called in the meantime and
490          * will set ws->active.  Then, ws->active may be cleared immediately
491          * by the __pm_relax() called from the timer function, but in such a
492          * case ws->relax_count will be different from ws->active_count.
493          */
494         if (ws->relax_count != ws->active_count) {
495                 ws->relax_count--;
496                 return;
497         }
498
499         ws->active = false;
500
501         now = ktime_get();
502         duration = ktime_sub(now, ws->last_time);
503         ws->total_time = ktime_add(ws->total_time, duration);
504         if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))
505                 ws->max_time = duration;
506
507         ws->last_time = now;
508         del_timer(&ws->timer);
509         ws->timer_expires = 0;
510
511         if (ws->autosleep_enabled)
512                 update_prevent_sleep_time(ws, now);
513
514         /*
515          * Increment the counter of registered wakeup events and decrement the
516          * couter of wakeup events in progress simultaneously.
517          */
518         cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);
519         trace_wakeup_source_deactivate(ws->name, cec);
520
521         split_counters(&cnt, &inpr);
522         if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
523                 wake_up(&wakeup_count_wait_queue);
524 }

这个函数前面我们就不看了,主要看下 521行到523行,这里split_counters实现如下
 32 static atomic_t combined_event_count = ATOMIC_INIT(0);
 33
 34 #define IN_PROGRESS_BITS        (sizeof(int) * 4)
 35 #define MAX_IN_PROGRESS         ((1 << IN_PROGRESS_BITS) - 1)
 36
 37 static void split_counters(unsigned int *cnt, unsigned int *inpr)
 38 {
 39         unsigned int comb = atomic_read(&combined_event_count);                                                                            
 40
 41         *cnt = (comb >> IN_PROGRESS_BITS);
 42         *inpr = comb & MAX_IN_PROGRESS;
 43 }

这个原子操作不就是得到了刚才我们++combined_event_count记录的wake_lock数量的变量吗?取得32位数上的高16位数啊,这样的话难道每次都是高16位存的都是新值,而后16位都是旧值吗?这个还需要研究研究!然后我们看下后两行

522         if (!inpr && waitqueue_active(&wakeup_count_wait_queue))
523                 wake_up(&wakeup_count_wait_queue);

这个就是判断wake_locks数,等待队列是否活动,然后唤醒等待队列,这个等待队列到底在哪里了?我们往后再说,这里先卖个关子 哇哈哈!

好了 现在我们wake_lock和unwake_lock都已经说了,但是他们和suspend有毛关系呢?我们继续分析state这个函数。

如果我们执行echo mem > /sys/power/state 会发生什么? 没错,手机屏幕会灭,手机会进入了earlysuspend状态,呵呵,这就是我分析第二片文章为什么要特意说下farmework 到 native  怎么把值写到这个节点的必要性了。下面我们看看函数

301 static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
302                            const char *buf, size_t n)
303 {
304         suspend_state_t state;
305         int error;
306
307         error = pm_autosleep_lock();
308         if (error)
309                 return error;
310
311         if (pm_autosleep_state() > PM_SUSPEND_ON) {
312                 error = -EBUSY;
313                 goto out;
314         }
315
316         state = decode_state(buf, n);
317         if (state < PM_SUSPEND_MAX) {
318 #ifdef CONFIG_EARLYSUSPEND
319                 if (state == PM_SUSPEND_ON || valid_state(state)) {
320                         error = 0;
321                         request_suspend_state(state);
322                 }
323 #else
324                 error = pm_suspend(state);
325 #endif
326
327 }
328         else if (state == PM_SUSPEND_MAX)
329                 error = hibernate();
330         else
331                 error = -EINVAL;
332
333  out:
334         pm_autosleep_unlock();
335         return error ? error : n;
336 }

首先判断系统是不是正在suspend的状态,然后检查写入的值是否有效,如果有效这里分支就是为了支持earlysuspend过能和不带earlysuspend功能,然后调用request_suspend_state(state);这个函数。移动设备默认都带earlysuspend功能

208 void request_suspend_state(suspend_state_t new_state)
209 {
210         unsigned long irqflags;
211         int old_sleep;
212         suspend_state_t prev_state;
213
214         mutex_lock(&suspend_lock);
215         prev_state = suspend_state;
216         spin_lock_irqsave(&state_lock, irqflags);
217         old_sleep = state & SUSPEND_REQUESTED;
218         if (debug_mask & DEBUG_USER_STATE) {
219                 struct timespec ts;
220                 struct rtc_time tm;
221                 getnstimeofday(&ts);
222                 rtc_time_to_tm(ts.tv_sec, &tm);
223                 pr_info("request_suspend_state: %s (%d->%d) at %lld "
224                         "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
225                         new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
226                         prev_state, new_state,
227                         ktime_to_ns(ktime_get()),
228                         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
229                         tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
230         }
231         if (!old_sleep && new_state != PM_SUSPEND_ON) {
232                 state |= SUSPEND_REQUESTED;
233                 queue_up_early_suspend_work(&early_suspend_work);
234         } else if (old_sleep && new_state == PM_SUSPEND_ON) {
235                 state &= ~SUSPEND_REQUESTED;
236                 __pm_stay_awake(early_suspend_ws);
237                 queue_up_early_suspend_work(&late_resume_work);
238         }
239         suspend_state = new_state;
240         spin_unlock_irqrestore(&state_lock, irqflags);
241         mutex_unlock(&suspend_lock);
242 }

这个函数主要就是区分是要走earlysuspend 还是lateresume,然后调用&early_suspend_work)这个工作队列,其实就是调用early_suspend函数
136 static void early_suspend(struct work_struct *work)
137 {
138         struct early_suspend *pos;
139         unsigned long irqflags;
140         int abort = 0;
141
142         mutex_lock(&early_suspend_lock);
143         spin_lock_irqsave(&state_lock, irqflags);
144         if (state == SUSPEND_REQUESTED)
145                 state |= SUSPENDED;
146         else
147                 abort = 1;
148         spin_unlock_irqrestore(&state_lock, irqflags);
149
150         if (abort) {
151                 if (debug_mask & DEBUG_SUSPEND)
152                         pr_info("early_suspend: abort, state %d\n", state);
153                 mutex_unlock(&early_suspend_lock);
154                 goto abort;
155         }
156
157         if (debug_mask & DEBUG_SUSPEND)
158                 pr_info("early_suspend: call handlers\n");
159         list_for_each_entry(pos, &early_suspend_handlers, link) {
160                 if (pos->suspend != NULL)
161                         pos->suspend(pos);
162         }
163         mutex_unlock(&early_suspend_lock);
164
165         if (debug_mask & DEBUG_SUSPEND)
166                 pr_info("early_suspend: after call handlers\n");
167         /* just wake up flusher to start write back and don't wait it finished*/
168         wakeup_flusher_threads(0, WB_REASON_SYNC);
169 abort:
170         spin_lock_irqsave(&state_lock, irqflags);
171         if (state == SUSPEND_REQUESTED_AND_SUSPENDED)
172                 __pm_relax(early_suspend_ws);
173         spin_unlock_irqrestore(&state_lock, irqflags);
174         mod_timer(&monitor_timer, jiffies + monitor_period*HZ);
175         queue_work(suspend_wq, &suspend_work);
176 }

这里就是逐个调用平台注册过得earlysuspend函数,按照leave来调用哦,注册时候会定义,然后系统会调用 queue_work(suspend_wq, &suspend_work);这个工作队列,这个其实就是try_to_suspend,

68 static void try_to_suspend(struct work_struct *work)
 69 {
 70         unsigned int initial_count, final_count;
 71
 72         if (!pm_get_wakeup_count(&initial_count, true) ||
 73             !alarm_pm_wake_check())
 74                 goto queue_again;
 75
 76         mutex_lock(&suspend_lock);
 77
 78         if (!pm_save_wakeup_count(initial_count)) {
 79                 mutex_unlock(&suspend_lock);
 80                 goto queue_again;
 81         }
 82         del_timer_sync(&monitor_timer);
 83
 84         if (suspend_state == PM_SUSPEND_ON) {
 85                 mutex_unlock(&suspend_lock);
 86                 return;
 87         }
 88
 89         if (suspend_state >= PM_SUSPEND_MAX)
 90                 hibernate();
 91         else
 92                 pm_suspend(suspend_state);
 93
 94         mutex_unlock(&suspend_lock);
 95
 96         if (!pm_get_wakeup_count(&final_count, false))    
 97                 goto queue_again;
 98
 99         /*
100          * If the wakeup occured for an unknown reason, wait to prevent the
101          * system from trying to suspend and waking up in a tight loop.
102          */
103         if (final_count == initial_count)
104                 schedule_timeout_uninterruptible(HZ / 2);
105
106 queue_again:
107         queue_work(suspend_wq, &suspend_work);
108 }

函数嘴开始就执行了 pm_get_wakeup_count()这个函数,这个函数:

722 bool pm_get_wakeup_count(unsigned int *count, bool block)
723 {
724         unsigned int cnt, inpr;
725
726         if (block) {
727                 DEFINE_WAIT(wait);
728
729                 for (;;) {
730                         prepare_to_wait(&wakeup_count_wait_queue, &wait,
731                                         TASK_INTERRUPTIBLE);
732                         split_counters(&cnt, &inpr);
733                         if (inpr == 0 || signal_pending(current))
734                                 break;
735
736                         schedule();
737                 }
738                 finish_wait(&wakeup_count_wait_queue, &wait);
739         }
740
741         split_counters(&cnt, &inpr);
742         *count = cnt;
743         return !inpr;
744 }

看到了刚才我们介绍unwake_lock函数的时候碰见的wakeup_count_wait_queue等待队列了吧,没错,这里就循环等待是不是有应用或者driver拿着锁,如果拿着就调度出去做别的事情,等待unwake_lock的显示唤醒队列,然后再去检查split_counters(&cnt, &inpr);锁数量,如果没有锁了,OK,我们跳出循环,准备进入下面的suspend流程,到这里是不是就算解释清楚,wakelock和suspend的关系了呢?上层 中层 底层调用基本上已经联系一起了,我会在下篇文章继续分析suspend的流程!

还是一样,本人小菜鸟一名,如果不对,请指出,谢谢!















阅读(6857) | 评论(2) | 转发(4) |
给主人留下些什么吧!~~

c语言达人2014-02-28 16:54:43

sp19881102:不对唉,我这边看到的earlysuspend貌似不是走这个流程的唉。

啊  走的哪里呢? 一起探讨下,对于HAL层上的代码我不熟悉,学习学习啊

回复 | 举报

sp198811022014-02-19 10:40:05

不对唉,我这边看到的earlysuspend貌似不是走这个流程的唉。