2011年10月1日 星期六

Android在標準linux基礎上對休眠喚醒的實現(二)(轉貼)


三、 kernel層源碼解析- wakelock的重要地位
wakelockandroid的休眠喚醒機制中扮演著及其重要的角色,主要源碼位於文件:
kernel/kernel/power/wakelock.c
kernel/include/linux/wakelock.h中。

wakelocks_init()函數所做的工作是整個wakelock可以工作起來的基礎,所有這裡先說說這個函數。
static int __init wakelocks_init (void)
{
       int ret;
       int i;

       for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
              INIT_LIST_HEAD(&active_wake_locks[i]);
       //初始化active_wake_locks數組中的兩個類型鎖鍊錶: WAKE_LOCK_SUSPEND,WAKE_LOCK_IDLE

#ifdef CONFIG_WAKELOCK_STAT         // defined
       wake_lock_init(& deleted_wake_locks , WAKE_LOCK_SUSPEND,
                     "deleted_wake_locks");
       //初始化wakelock deleted_wake_locks,同時將其加入到非活動鎖鍊錶中
#endif
       wake_lock_init(& main_wake_lock , WAKE_LOCK_SUSPEND, "main");
       wake_lock_init(& sys_sync_wake_lock , WAKE_LOCK_SUSPEND, "sys_sync");
       wake_lock(&main_wake_lock);
       wake_lock_init(& unknown_wakeup , WAKE_LOCK_SUSPEND, "unknown_wakeups");
       //初始化wakelock: main, sys_sync, unknown_wakeups,同時將其加入到非活動鎖鍊錶中
       //main_wake_lock加鎖
      
       ret = platform_device_register(&power_device) ;
       if (ret) {
              pr_err("[wakelocks_init]: platform_device_register failed/n");
              goto err_platform_device_register;
       }
       ret = platform_driver_register(&power_driver) ;
       if (ret) {
              pr_err("[wakelocks_init]: platform_driver_register failed/n");
              goto err_platform_driver_register;
       }

       //新建工作隊列和工作者內核線程: sys_sync_work_queue, fs_sync
       // suspend_work_queue, suspend                                                     
       sys_sync_work_queue = create_singlethread_workqueue("fs_sync");
       if (sys_sync_work_queue == NULL) {
              pr_err("[wakelocks_init] fs_sync workqueue create failed/n");
       }

       suspend_work_queue = create_singlethread_workqueue("suspend");
       if (suspend_work_queue == NULL) {
              ret = -ENOMEM;
              goto err_suspend_work_queue;
       }

#ifdef CONFIG_WAKELOCK_STAT
       proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);
       //創建proc接口
#endif

       return 0;

err_suspend_work_queue:
       platform_driver_unregister(&power_driver);
err_platform_driver_register:
       platform_device_unregister(&power_device);
err_platform_device_register:
       wake_lock_destroy(&unknown_wakeup);
       wake_lock_destroy(&main_wake_lock);
#ifdef CONFIG_WAKELOCK_STAT
       wake_lock_destroy(&deleted_wake_locks);
#endif
       return ret;
}

可以看到該初始化函數中新建了幾個wakelock: deleted_wake_locksmain_wake_locksys_sync_wake_lockunknown_wakeup,他們全部都是WAKE_LOCK_SUSPEND類型的wakelock,說到這裡不得不提到wakelock的兩種類型了:
1.      WAKE_LOCK_SUSPEND –這種鎖如果被某個task持有,那麼系統將無法進入休眠。
2.      WAKE_LOCK_IDLE –這種鎖不會影響到系統進入休眠,但是如果這種鎖被持有,那麼系統將無法進入idle空閒模式。

不過常用的所類型還是WAKE_LOCK_SUSPEND,包括userwakelock.c提供給用戶空間的新建wakelock的接口,都是建立的第一種鎖。另外系統為了分開管理這兩種不同類型的鎖,建立了兩個鍊錶來統一鏈接不同類型的鎖:active_wake_locks[],這個是具有兩個鍊錶頭的數組,元素0是掛接WAKE_LOCK_SUSPEND類型的鎖,而元素1就是掛接WAKE_LOCK_IDLE類型的wakelock了。

       接著上面說,這個初始化函數新建這些鎖之後,直接將主鎖(main_wake_lock)給上鎖了,其餘都是非鎖狀態。新建wakelock使用函數wake_lock_init(),該函數設置鎖的名字,類型,最後將新建的鎖掛接到一個專門鏈接這些非鎖狀態的鍊錶inactive_locks(新建的wakelock初期都是出於非鎖狀態的,除非顯示調用函數wake_lock來上鎖)。接著如果使用函數wake_lock()來給特定的wakelock上鎖的話,會將該鎖從鍊錶inactive_locks上移動到對應類型的專用鍊錶上active_wake_locks[type]上。
      
       wakelock有兩種形式的鎖:超時鎖和非超時鎖,這兩種形式的鎖都是使用函數wake_lock_init()來初始化,只是在上鎖的時候會有一點點差別,超時鎖使用函數wake_lock_timeout(),而非超時鎖使用函數wake_lock(),這個兩個函數會最終調用到同一個函數wake_lock_internal(),該函數依靠傳入的 ​​不同參數來選擇不同的路徑來工作。值得注意的是,非超時鎖必須手工解鎖,否則係統永遠不能進入睡眠。下面是wake_lock_internal()函數的片段:
       if (!(lock->flags & WAKE_LOCK_ACTIVE))
              lock->flags |= WAKE_LOCK_ACTIVE; // wakelock狀態為inactive,則更改為active
       …
       if ( has_timeout ) { // wake_lock_timeout() 會傳入1
              if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
                     pr_info("[wake_lock_internal]: %s, type %d, timeout %ld.%03lu/n",
                            lock->name, type, timeout / HZ,
                            (timeout % HZ) * MSEC_PER_SEC / HZ);
              lock->expires = jiffies + timeout; //       設置超時時間
              lock->flags |= WAKE_LOCK_AUTO_EXPIRE; //       超時鎖標誌
              list_add_tail(&lock->link, &active_wake_locks[type]) ;
       }
    // acquire a non-timeout wakelock添加一個非超時鎖
       else { // wake_lock ()      會傳入0
              if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
                     pr_info("[wake_lock_internal]: %s, type %d/n", lock->name, type);
              lock->expires = LONG_MAX; //    設置成超時時間最大值
              lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE; //非超時鎖標誌
              list_add(&lock->link, &active_wake_locks[type]);
              //將剛剛設置的非超時鎖加到對應類型的活動鎖鍊錶中
       }
      
       解鎖的時候,這兩種形式的鎖所使用函數都是一樣了:wake_unlock(),該函數中會首先作如下操作:
       lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);
       //清除鎖活動標誌和自動超時標誌
       list_del(&lock->link); //   從鎖對應的活動鍊錶上摘除
       list_add(&lock->link, &inactive_locks);   
//unlock的鎖掛接到非活動鍊錶inactive_locks

前面已經說了只有類型為WAKE_LOCK_SUSPENDwakelock被上鎖才會阻止系統進入suspend,那麼也就是說只要鍊錶active_wake_locks[WAKE_LOCK_SUSPEND]NULL,那麼系統就可以執行suspend的流程了。Androidlinux的改造,讓其可以在三種情況下進入linux的標準suspend的流程:
1.               wake_unlock(),這個應該是最容易想到的,只要係統有對WAKE_LOCK_SUSPEND類型的wakelock解鎖的動作,都有可能會進入suspend流程開始休眠,為什麼是有可能呢?因為可能還有超時鎖沒有被超時解鎖。下面看一下代碼片段:
void wake_unlock(struct wake_lock *lock)
{
       …
       if ( type == WAKE_LOCK_SUSPEND ) //貌似只在處理這個類型的wakelock
    {
              long has_lock = has_wake_lock_locked(type) ;
              //這個函數蠻重要,它來檢查type類型的鍊錶上是否還有鎖被上鎖了。
        //其返回值如果是0,說明沒有該類型的鎖被持有了;返回非0表明就是這個類型的活動鍊錶上還存在超時鎖但是沒有非超時鎖了,這個返回值就是當前時間距離最後超時的鎖超時時間的jiffies值;如果返回-1,那表明還有該類型的非超時鎖被持有。
        if (wakelock_debug_mask & DEBUG_WAKE_LOCK)
            pr_info("[wake_unlock]: has_​​lock = 0x%x/n" , has_​​lock);       
              if ( has_lock > 0 ) {
                     if (wakelock_debug_mask & DEBUG_EXPIRE)
                            pr_info("[wake_unlock]: %s, start expire timer, "
                                   "%ld/n", lock->name, has_​​lock);
                     mod_timer(&expire_timer, jiffies + has_​​lock);
//修改定時器的超時值並add該定時器
              }
              else //已經沒有超時鎖了
              {
                     if ( del_timer(&expire_timer) ) //            刪除定時器
                            if (wakelock_debug_mask & DEBUG_EXPIRE)
                                   pr_info("[wake_unlock]: %s, stop expire "
                                          "timer/n", lock->name);
                     if ( has_lock == 0 )
// !=0,表明還有該類型的非超時鎖被持有,現在還不能進入suspend
                     {
                      pr_info("[wake_unlock]: (%s) suspend_work_queue suspend_work/n" , lock->name);
                            queue_work(suspend_work_queue, &suspend_work);
                            //提交suspend的工作項,開始執行標準linuxsuspend流程
                     }    
              }
              …
       }
       spin_unlock_irqrestore(&list_lock, irqflags);
}

2.             超時鎖超時之後,定時器的回調函數會執行會查看是否有其他的wakelock,如果沒有,就在這裡讓系統進入睡眠。
static void expire_wake_locks (unsigned long data)
{
       long has_​​lock;
       unsigned long irqflags;
       if (debug_mask & DEBUG_EXPIRE)
              pr_info("expire_wake_locks: start/n");
       spin_lock_irqsave(&list_lock, irqflags);
       if (debug_mask & DEBUG_SUSPEND)
              print_active_locks(WAKE_LOCK_SUSPEND);
       has_lock = has_wake_lock_locked(WAKE_LOCK_SUSPEND) ;
       if (debug_mask & DEBUG_EXPIRE)
              pr_info("expire_wake_locks: done, has_​​lock %ld/n", has_​​lock);
       if ( has_lock == 0 )
//如果沒有SUSPEND類型的wakelock處於active,那麼將調用suspend
              queue_work(suspend_work_queue, &suspend_work);
       spin_unlock_irqrestore(&list_lock, irqflags);
}
       static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
列出以下一個重要的函數源碼:
static long has_wake_lock_locked (int type)
{
       struct wake_lock *lock, *n;
       long max_timeout = 0;

       BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
       list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) {
              if ( lock->flags & WAKE_LOCK_AUTO_EXPIRE ) {
                     long timeout = lock->expires - jiffies;
                     if (timeout <= 0)
                            expire_wake_lock(lock);
                     else if (timeout > max_timeout)
                            max_timeout = timeout;
              } else
                     return -1;
       }
       return max_timeout;
}

3.               這個可能有人覺得匪夷所思,就是在wake_lock{_ _timeout}()函數中,調用了內部函數wake_lock_internal()。這裡只有在對超時鎖上鎖的時候才有可能進入休眠,如果對一個費超時鎖上鎖的話,那麼就沒有必要去檢查活動鍊錶了。
static void wake_lock_internal(
       struct wake_lock *lock, long timeout, int has_​​timeout)
{
if ( type == WAKE_LOCK_SUSPEND ) {
              current_event_num++;
#ifdef CONFIG_WAKELOCK_STAT
              if (lock == &main_wake_lock)
                     update_sleep_wait_stats_locked(1);
              else if (!wake_lock_active(&main_wake_lock))
                     update_sleep_wait_stats_locked(0);
#endif
              if (has_timeout) //   超時鎖的時候傳進來的是1
                     expire_in = has_​​wake_lock_locked(type);
                     //檢查當前鎖類型鍊錶上是否還有鎖處於active的狀態,無返回0
              else
                     expire_in = -1;
//如果是非超時鎖的話,這裡直接賦值-1,省去了活動鍊錶檢查步驟了
              if ( expire_in > 0 ) {
                     if (debug_mask & DEBUG_EXPIRE)
                            pr_info("wake_lock: %s, start expire timer, "
                                   "%ld/n", lock->name, expire_in);
                     // modify the time wakelock is expired
                     mod_timer(&expire_timer, jiffies + expire_in);
              } else {
                     if ( del_timer(&expire_timer) )
                            if (debug_mask & DEBUG_EXPIRE)
                                   pr_info("wake_lock: %s, stop expire timer/n",
                                          lock->name);
                     if ( expire_in == 0 ) //沒有鎖處於active狀態後,準備調用suspend
                     {
                      pr_info("[wake_lock]: suspend_work_queue suspend_work/n ");
                            queue_work(suspend_work_queue, &suspend_work) ;
                     }
              }
       }
       spin_unlock_irqrestore(&list_lock, irqflags);
}

下面是suspend的工作項,經過上面三種情況的檢查,ok之後將會提交該工作項給工作隊列suspend_work_queue,如下:
static void suspend(struct work_struct *work)
{
       int ret;
       int entry_event_num;

    // there are still some wakelock
       if (has_wake_lock(WAKE_LOCK_SUSPEND)) {
              if (wakelock_debug_mask & DEBUG_SUSPEND)
                     pr_info("[suspend]: abort suspend/n");
              return;
       }

       entry_event_num = current_event_num;
       sys_sync();
       if (debug_mask & DEBUG_SUSPEND)
              pr_info("suspend: enter suspend/n");
       ret = pm_suspend(requested_suspend_state) ;
       // requested_suspend_state這個全局變量在函數request_suspend_state()中被設置,也就是執行了eraly suspend或者late resume之後,主要是為suspend保留請求的省電狀態。
       if (debug_mask & DEBUG_EXIT_SUSPEND) {
              struct timespec ts;
              struct rtc_time tm;
              getnstimeofday(&ts);
              rtc_time_to_tm(ts.tv_sec, &tm);
              pr_info("suspend: exit suspend, ret = %d "
                     "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)/n", ret,
                     tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
                     tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
       }
       if (current_event_num == entry_event_num) {
              if (debug_mask & DEBUG_SUSPEND)
                     pr_info("suspend: pm_suspend returned with no event/n");
              wake_lock_timeout(&unknown_wakeup, HZ / 2);
       }
}
static DECLARE_WORK(suspend_work, suspend);

@kernel/ke​​rnel/power/suspend.c
int pm_suspend (suspend_state_t state)
{
       if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)
              return enter_state(state);
              //標準linuxsuspend流程函數
return -EINVAL;
}
EXPORT_SYMBOL(pm_suspend);

Wakelock的機制被文件userwakelock.c中的code封裝成了sys的接口sys/power/wake_locksys/power/wake_unlock文件,那麼上層如果需要新建wakelock或者註銷wakelock,或者是解鎖wakelock,都是操作這兩個sys接口文件。

沒有留言:

張貼留言