久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

 找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 11570|回復: 2
打印 上一主題 下一主題
收起左側

android 休眠喚醒機制分析

[復制鏈接]
跳轉到指定樓層
樓主
ID:71922 發表于 2015-1-10 19:07 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式

Android的休眠喚醒主要基于wake_lock機制,只要系統中存在任一有效的wake_lock,系統就不能進入深度休眠,但可以進行設備的淺度休眠操作。wake_lock一般在關閉lcd、tp但系統仍然需要正常運行的情況下使用,比如聽歌、傳輸很大的文件等。本文主要分析driver層wake_lock的實現。

一、wake_lock 定義和接口

[cpp] view plaincopy

    enum {  
        WAKE_LOCK_SUSPEND, // 阻止進入深度休眠模式  
        WAKE_LOCK_IDLE,    // 阻止進入空閑模式  
        WAKE_LOCK_TYPE_COUNT  
    };  

    struct wake_lock {  
    #ifdef CONFIG_HAS_WAKELOCK  
        struct list_head    link;     // 鏈表節點  
        int                 flags;    // 標志  
        const char         *name;     // 名稱  
        unsigned long       expires;  // 超時時間  
    #ifdef CONFIG_WAKELOCK_STAT  
        struct {  
            int             count;         // 使用計數  
            int             expire_count;  // 超時計數  
            int             wakeup_count;  // 喚醒計數  
            ktime_t         total_time;    // 鎖使用時間  
            ktime_t         prevent_suspend_time;  // 鎖阻止休眠的時間  
            ktime_t         max_time;      // 鎖使用時間最長的一次  
            ktime_t         last_time;     // 鎖上次操作時間  
        } stat;  
    #endif  
    #endif  
    };  

可以看到wake_lock按功能分為休眠鎖和空閑鎖兩種類型,用于阻止系統進入深度休眠模式或者空閑模式。wake_lock的主要部件有鎖名稱、鏈表節點、標志位、超時時間,另外還有一個內嵌的結構用于統計鎖的使用信息。接下來我們看看wake_lock對外提供的操作接口:

1、內核空間接口

[cpp] view plaincopy

    void wake_lock_init(struct wake_lock *lock, int type, const char *name);  
    void wake_lock_destroy(struct wake_lock *lock);  
    void wake_lock(struct wake_lock *lock);  
    void wake_lock_timeout(struct wake_lock *lock, long timeout);  
    void wake_unlock(struct wake_lock *lock);  

其中wake_lock_init()用于初始化一個新鎖,type參數指定了鎖的類型;wake_lock_destroy()則注銷一個鎖;wake_lock()和wake_lock_timeout()用于將初始化完成的鎖激活,使之成為有效的永久鎖或者超時鎖;wake_unlock()用于解鎖使之成為無效鎖。另外還有兩個接口:

[cpp] view plaincopy

    int wake_lock_active(struct wake_lock *lock);  
    long has_wake_lock(int type);  

其中wake_lock_active()用于判斷鎖當前是否有效,如果有效則返回非0值;has_wake_lock()用于判斷系統中是否還存在有效的type型鎖,如果存在超時鎖則返回最長的一個鎖的超時時間,如果存在永久鎖則返回-1,如果系統中不存在有效鎖則返回0。

2、用戶空間接口

wake_lock向用戶空間提供了兩個文件節點用于申請鎖和解鎖:

[cpp] view plaincopy

    // wack_lock文件的讀函數,顯示用戶空間定義的有效鎖  
    ssize_t wake_lock_show(  
        struct kobject *kobj, struct kobj_attribute *attr, char *buf)  
    {  
        char *s = buf;  
        char *end = buf + PAGE_SIZE;  
        struct rb_node *n;  
        struct user_wake_lock *l;  

        mutex_lock(&tree_lock);  

        for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {  
            l = rb_entry(n, struct user_wake_lock, node);  
            if (wake_lock_active(&l->wake_lock))  
                s += scnprintf(s, end - s, "%s ", l->name);  
        }  
        s += scnprintf(s, end - s, "\n");  

        mutex_unlock(&tree_lock);  
        return (s - buf);  
    }  

    // wack_lock文件的寫函數,初始化并激活用戶空間定義的鎖  
    ssize_t wake_lock_store(  
        struct kobject *kobj, struct kobj_attribute *attr,  
        const char *buf, size_t n)  
    {  
        long timeout;  
        struct user_wake_lock *l;  

        mutex_lock(&tree_lock);  
        l = lookup_wake_lock_name(buf, 1, &timeout);  
        if (IS_ERR(l)) {  
            n = PTR_ERR(l);  
            goto bad_name;  
        }  

        if (debug_mask & DEBUG_ACCESS)  
            pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);  

        if (timeout)  
            wake_lock_timeout(&l->wake_lock, timeout);  
        else  
            wake_lock(&l->wake_lock);  
    bad_name:  
        mutex_unlock(&tree_lock);  
        return n;  
    }  

    // wack_unlock文件的讀函數,顯示用戶空間的無效鎖  
    ssize_t wake_unlock_show(  
        struct kobject *kobj, struct kobj_attribute *attr, char *buf)  
    {  
        char *s = buf;  
        char *end = buf + PAGE_SIZE;  
        struct rb_node *n;  
        struct user_wake_lock *l;  

        mutex_lock(&tree_lock);  

        for (n = rb_first(&user_wake_locks); n != NULL; n = rb_next(n)) {  
            l = rb_entry(n, struct user_wake_lock, node);  
            if (!wake_lock_active(&l->wake_lock))  
                s += scnprintf(s, end - s, "%s ", l->name);  
        }  
        s += scnprintf(s, end - s, "\n");  

        mutex_unlock(&tree_lock);  
        return (s - buf);  
    }  

    // wack_unlock文件的寫函數,用于用戶空間解鎖  
    ssize_t wake_unlock_store(  
        struct kobject *kobj, struct kobj_attribute *attr,  
        const char *buf, size_t n)  
    {  
        struct user_wake_lock *l;  

        mutex_lock(&tree_lock);  
        l = lookup_wake_lock_name(buf, 0, NULL);  
        if (IS_ERR(l)) {  
            n = PTR_ERR(l);  
            goto not_found;  
        }  

        if (debug_mask & DEBUG_ACCESS)  
            pr_info("wake_unlock_store: %s\n", l->name);  

        wake_unlock(&l->wake_lock);  
    not_found:  
        mutex_unlock(&tree_lock);  
        return n;  
    }  

    power_attr(wake_lock);  
    power_attr(wake_unlock);  

這兩個文件節點分別為"/sys/power/wake_lock"和"/sys/power/wake_unlock",應用程序可以根據HAL層的接口讀寫這兩個節點。
二、wake_lock 實現
在linux/kernel/power/wakelock.c中我們可以看到wake_lock的實現代碼,首先看看其定義的一些初始化信息:

[cpp] view plaincopy

    #define WAKE_LOCK_TYPE_MASK              (0x0f)     // 鎖類型標志掩碼  
    #define WAKE_LOCK_INITIALIZED            (1U << 8)  // 鎖已經初始化標志  
    #define WAKE_LOCK_ACTIVE                 (1U << 9)  // 鎖有效標志  
    #define WAKE_LOCK_AUTO_EXPIRE            (1U << 10) // 超時鎖標志  
    #define WAKE_LOCK_PREVENTING_SUSPEND     (1U << 11) // 正在阻止休眠標志  

    static DEFINE_SPINLOCK(list_lock);  // 讀寫鎖鏈表的自旋鎖  
    static LIST_HEAD(inactive_locks);   // 內核維護的無效鎖鏈表  
    static struct list_head active_wake_locks[WAKE_LOCK_TYPE_COUNT];  // 有效鎖鏈表  
    static int current_event_num;       // 休眠鎖使用計數器  
    struct workqueue_struct *suspend_work_queue;  // 執行系統休眠的工作隊列  
    struct workqueue_struct *sys_sync_work_queue; // 執行系統同步的工作隊列  
    struct wake_lock main_wake_lock;              // 內核休眠鎖  
    struct wake_lock sys_sync_wake_lock;          // 緩存同步鎖  
    suspend_state_t requested_suspend_state = PM_SUSPEND_MEM;  // 系統休眠狀態  
    static struct wake_lock unknown_wakeup;       // 未知鎖  

在后面的分析中我們會看到這些變量的具體用途。

1、wake_lock系統初始化

[cpp] view plaincopy

    static int __init wakelocks_init(void)  
    {  
        int ret;  
        int i;  
        // 初始化有效鎖鏈表,內核維護了2個有效鎖鏈表  
        // WAKE_LOCK_SUSPEND 用于阻止進入深度休眠模式  
        // WAKE_LOCK_IDLE    用于阻止進入空閑模式  
        for (i = 0; i < ARRAY_SIZE(active_wake_locks); i++)  
            INIT_LIST_HEAD(&active_wake_locks[ i]);  

    #ifdef CONFIG_WAKELOCK_STAT  
        // 初始化deleted_wake_locks  
        wake_lock_init(&deleted_wake_locks, WAKE_LOCK_SUSPEND,  
                "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");  

        // 注冊power_device,power_driver  
        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;  
        }  
        // 創建fs_sync內核進程  
        sys_sync_work_queue = create_singlethread_workqueue("fs_sync");  
        if (sys_sync_work_queue == NULL) {  
            pr_err ("fs_sync workqueue create failed.\n");  
        }  
        // 創建suspend內核進程  
        suspend_work_queue = create_singlethread_workqueue("suspend");  
        if (suspend_work_queue == NULL) {  
            ret = -ENOMEM;  
            goto err_suspend_work_queue;  
        }  

    #ifdef CONFIG_WAKELOCK_STAT  
        // 在proc下創建wakelocks文件  
        proc_create("wakelocks", S_IRUGO, NULL, &wakelock_stats_fops);  
    #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;  
    }  
    core_initcall(wakelocks_init);  

可以看到內核通過core_initcall調用了wake_lock系統的初始化函數,函數首先初始化了兩個有效鎖的鏈表,用于管理系統中的有效鎖;接下來初始化了deleted_wake_locks用于處理統計信息,main_wake_lock用于鎖定內核(系統啟動時會激活這個鎖,深度休眠時需要釋放這個鎖),sys_sync_wake_lock用于淺度休眠階段同步緩存時阻止內核進入深度休眠,unknown_wakeup用于喚醒時延遲0.5s進入下一次可能的深度休眠;還注冊了一個platform_device用于深度休眠階段檢測是否存在有效鎖;后面創建了內核進程fs_sync用于淺度休眠階段同步緩存,內核進程suspend用于進行淺度休眠和深度休眠;還在/proc下面創建了wakelocks節點用于顯示wake_lock的統計信息。

2、wake_lock初始化

[cpp] view plaincopy

    void wake_lock_init(struct wake_lock *lock, int type, const char *name)  
    {  
        unsigned long irqflags = 0;  
        // 初始化名稱  
        if (name)  
            lock->name = name;  
        BUG_ON(!lock->name);  

        if (debug_mask & DEBUG_WAKE_LOCK)  
            pr_info("wake_lock_init name=%s\n", lock->name);  
    #ifdef CONFIG_WAKELOCK_STAT  
        lock->stat.count = 0;  
        lock->stat.expire_count = 0;  
        lock->stat.wakeup_count = 0;  
        lock->stat.total_time = ktime_set(0, 0);  
        lock->stat.prevent_suspend_time = ktime_set(0, 0);  
        lock->stat.max_time = ktime_set(0, 0);  
        lock->stat.last_time = ktime_set(0, 0);  
    #endif  
        // 初始化flag  
        lock->flags = (type & WAKE_LOCK_TYPE_MASK) | WAKE_LOCK_INITIALIZED;  
        // 初始化鏈表節點  
        INIT_LIST_HEAD(&lock->link);  
        spin_lock_irqsave(&list_lock, irqflags);  
        // 將鎖加入無效鎖鏈表  
        list_add(&lock->link, &inactive_locks);  
        spin_unlock_irqrestore(&list_lock, irqflags);  
    }  
    EXPORT_SYMBOL(wake_lock_init);  

其中參數lock為被初始化對象,type代表鎖的類型,name表示鎖的名稱, 函數主要初始化鎖的名稱并設置 WAKE_LOCK_INITIALIZED 標志位,并將鎖加入無效鎖鏈表inactive_locks,當需要使用鎖的時候通過wake_lock()或者wake_lock_timeout()激活該鎖:

[cpp] view plaincopy

    // 根據參數激活鎖  
    static void wake_lock_internal(  
        struct wake_lock *lock, long timeout, int has_timeout)  
    {  
        int type;  
        unsigned long irqflags;  
        long expire_in;  

        spin_lock_irqsave(&list_lock, irqflags);  
        // 獲取鎖的類型  
        type = lock->flags & WAKE_LOCK_TYPE_MASK;  
        BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);  
        BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));  
    #ifdef CONFIG_WAKELOCK_STAT  
        if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {  
            if (debug_mask & DEBUG_WAKEUP)  
                pr_info("wakeup wake lock: %s\n", lock->name);  
            wait_for_wakeup = 0;  
            lock->stat.wakeup_count++;  
        }  
        if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&  
            (long)(lock->expires - jiffies) <= 0) {  
            wake_unlock_stat_locked(lock, 0);  
            lock->stat.last_time = ktime_get();  
        }  
    #endif  
        // 設置鎖有效的標志位  
        if (!(lock->flags & WAKE_LOCK_ACTIVE)) {  
            lock->flags |= WAKE_LOCK_ACTIVE;  
    #ifdef CONFIG_WAKELOCK_STAT  
            lock->stat.last_time = ktime_get();  
    #endif  
        }  
        // 將鎖從無效鎖鏈表中刪除  
        list_del(&lock->link);  
        // 如果是超時鎖  
        if (has_timeout) {  
            if (debug_mask & DEBUG_WAKE_LOCK)  
                pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",  
                    lock->name, type, timeout / HZ,  
                    (timeout % HZ) * MSEC_PER_SEC / HZ);  
            // 設置鎖超時時間,以當前jiffies為基準  
            lock->expires = jiffies + timeout;  
            // 設置鎖的超時鎖標志  
            lock->flags |= WAKE_LOCK_AUTO_EXPIRE;  
            // 將鎖加入有效鎖鏈表  
            list_add_tail(&lock->link, &active_wake_locks[type]);  
        } else {  // 如果是永久鎖  
            if (debug_mask & DEBUG_WAKE_LOCK)  
                pr_info("wake_lock: %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]);  
        }  
        // 如果是休眠鎖  
        if (type == WAKE_LOCK_SUSPEND) {  
            current_event_num++;  // 休眠鎖使用計數器加1  
    #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)  
                expire_in = has_wake_lock_locked(type);  
            else  
                expire_in = -1;  
            // 當前存在有效超時鎖,并且最長的一個到期時間間隔為expire_in  
            if (expire_in > 0) {  
                if (debug_mask & DEBUG_EXPIRE)  
                    pr_info("wake_lock: %s, start expire timer, "  
                        "%ld\n", lock->name, expire_in);  
                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)  // 無有效鎖  
                    queue_work(suspend_work_queue, &suspend_work);  
            }  
        }  
        spin_unlock_irqrestore(&list_lock, irqflags);  
    }  

    // 激活永久鎖  
    void wake_lock(struct wake_lock *lock)  
    {  
        wake_lock_internal(lock, 0, 0);  
    }  
    EXPORT_SYMBOL(wake_lock);  

    // 激活超時鎖  
    void wake_lock_timeout(struct wake_lock *lock, long timeout)  
    {  
        wake_lock_internal(lock, timeout, 1);  
    }  
    EXPORT_SYMBOL(wake_lock_timeout);  

可以看到激活過程都是通過調用wake_lock_internal()完成的,該函數首先完成一些統計信息的初始化,設置 WAKE_LOCK_ACTIVE 標志位并將鎖從無效鎖鏈表中移除;然后根據是否是超時鎖設置 WAKE_LOCK_AUTO_EXPIRE 標志位,并設置超時鎖的超時時間,再將鎖加入有效鎖鏈表;最后再根據鎖的類型判斷是否為休眠鎖,如果是休眠鎖且為超時鎖則通過has_wake_lock_locked()獲取系統中存在的超時鎖中時間最長的到期時間值,并以此值設置expire_timer,has_wake_lock_locked()返回0則表示系統中不存在有效鎖則啟動suspend進程開始進入深度休眠狀態。

3、expire_timer
[cpp] view plaincopy

    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)  
            // 則啟動深度休眠工作隊列  
            queue_work(suspend_work_queue, &suspend_work);  
        spin_unlock_irqrestore(&list_lock, irqflags);  
    }  
    // 定義timer,運行函數為expire_wake_locks  
    static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);  

該timer會在多個地方用到,在激活鎖的函數中注冊用于超時鎖到期后檢測系統的有效鎖狀態,如果系統不存在有效鎖了則啟動suspend進程。
4、suspend_work
[cpp] view plaincopy

    static void suspend(struct work_struct *work)  
    {  
        int ret;  
        int entry_event_num;  

        // 判斷系統是否還持有有效鎖,如果有則直接返回  
        if (has_wake_lock(WAKE_LOCK_SUSPEND)) {  
            if (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);  
        // 退出深度休眠,打印信息  
        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");  
            // 激活unknown_wakeup,0.5s超時  
            wake_lock_timeout(&unknown_wakeup, HZ / 2);  
        }  
    }  
    // 聲明工作隊列,運行函數為suspend  
    static DECLARE_WORK(suspend_work, suspend);  

聲明工作隊列用于內核深度休眠,可以看到一個正常的休眠流程會三次調用sys_sync()用于同步緩存(之前一次在淺度休眠,之后一次在深度休眠),然后調用pm_suspend()開始執行深度休眠流程。
5、has_wake_lock

[cpp] view plaincopy

    // 移除過期超時鎖  
    static void expire_wake_lock(struct wake_lock *lock)  
    {  
    #ifdef CONFIG_WAKELOCK_STAT  
        wake_unlock_stat_locked(lock, 1);  
    #endif  
        // 清除鎖有效和超時鎖標志  
        lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);  
        // 從當前鏈表中刪除  
        list_del(&lock->link);  
        // 加入無效鎖鏈表  
        list_add(&lock->link, &inactive_locks);  
        if (debug_mask & (DEBUG_WAKE_LOCK | DEBUG_EXPIRE))  
            pr_info("expired wake lock %s\n", lock->name);  
    }  

    // 打印有效鎖信息,調用者需持有list_lock  
    static void print_active_locks(int type)  
    {  
        struct wake_lock *lock;  
        bool print_expired = true;  

        BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);  
        // 遍歷有效鎖鏈表  
        list_for_each_entry(lock, &active_wake_locks[type], link) {  
            // 如果是超時鎖  
            if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) {  
                // 計算超時剩余時間  
                long timeout = lock->expires - jiffies;  
                if (timeout > 0)  
                    pr_info("active wake lock %s, time left %ld\n",  
                        lock->name, timeout);  
                else if (print_expired)  
                    pr_info("wake lock %s, expired\n", lock->name);  
            } else {  // 如果不是超時鎖  
                pr_info("active wake lock %s\n", lock->name);  
                if (!debug_mask & DEBUG_EXPIRE)  
                    print_expired = false;  
            }  
        }  
    }  

    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 // 如果不是超時鎖則返回-1  
                return -1;  
        }  
        return max_timeout;  
    }  

    // 判斷系統是否還持有有效鎖  
    long has_wake_lock(int type)  
    {  
        long ret;  
        unsigned long irqflags;  
        spin_lock_irqsave(&list_lock, irqflags);  
        // 開始判斷流程  
        ret = has_wake_lock_locked(type);  
        // 如果還有休眠鎖有效則打印狀態信息  
        if (ret && (debug_mask & DEBUG_SUSPEND) && type == WAKE_LOCK_SUSPEND)  
            print_active_locks(type);  
        spin_unlock_irqrestore(&list_lock, irqflags);  
        return ret;  
    }  

has_wake_lock()為系統判斷當前是否存在指定類型有效鎖的接口,在has_wake_lock_locked()中遍歷有效鎖鏈表,返回前面我們已經說明的值;并且打印所有有效鎖的狀態信息。
6、wake_unlock

[cpp] view plaincopy

    void wake_unlock(struct wake_lock *lock)  
    {  
        int type;  
        unsigned long irqflags;  
        spin_lock_irqsave(&list_lock, irqflags);  
        type = lock->flags & WAKE_LOCK_TYPE_MASK;  
    #ifdef CONFIG_WAKELOCK_STAT  
        // 更新鎖的狀態  
        wake_unlock_stat_locked(lock, 0);  
    #endif  
        if (debug_mask & DEBUG_WAKE_LOCK)  
            pr_info("wake_unlock: %s\n", lock->name);  
        // 清楚有效鎖和超時鎖標志  
        lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);  
        // 將鎖從有效鎖鏈表中移除加入無效鎖鏈表  
        list_del(&lock->link);  
        list_add(&lock->link, &inactive_locks);  
        // 如果是休眠鎖  
        if (type == WAKE_LOCK_SUSPEND) {  
            // 判斷系統當前是否還持有鎖  
            long has_lock = has_wake_lock_locked(type);  
            // 如果還持有鎖,設置timer到超時時間點觸發  
            if (has_lock > 0) {  
                if (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);  
            } else {  
                if (del_timer(&expire_timer))  // 刪除timer  
                    if (debug_mask & DEBUG_EXPIRE)  
                        pr_info("wake_unlock: %s, stop expire "  
                            "timer\n", lock->name);  
                if (has_lock == 0)  // 啟動深度休眠工作隊列  
                    queue_work(suspend_work_queue, &suspend_work);  
            }  
            // 如果是內核鎖  
            if (lock == &main_wake_lock) {  
                if (debug_mask & DEBUG_SUSPEND)  
                    // 打印當前有效鎖信息  
                    print_active_locks(WAKE_LOCK_SUSPEND);  
    #ifdef CONFIG_WAKELOCK_STAT  
                update_sleep_wait_stats_locked(0);  
    #endif  
            }  
        }  
        spin_unlock_irqrestore(&list_lock, irqflags);  
    }  
    EXPORT_SYMBOL(wake_unlock);  

該函數用于釋放一個鎖,首先將鎖從有效鎖鏈表中移除并加入無效鎖鏈表,并判斷系統是否還持有有效鎖,如果沒有則進入深度休眠流程。

7、wake_lock_active

[cpp] view plaincopy

    // 判斷鎖是否有效  
    int wake_lock_active(struct wake_lock *lock)  
    {  
        return !!(lock->flags & WAKE_LOCK_ACTIVE);  
    }  
    EXPORT_SYMBOL(wake_lock_active);  

8、wake_lock_destroy

[cpp] view plaincopy

    void wake_lock_destroy(struct wake_lock *lock)  
    {  
        unsigned long irqflags;  
        if (debug_mask & DEBUG_WAKE_LOCK)  
            pr_info("wake_lock_destroy name=%s\n", lock->name);  
        spin_lock_irqsave(&list_lock, irqflags);  
        // 清除已經初始化的標志  
        lock->flags &= ~WAKE_LOCK_INITIALIZED;  
    #ifdef CONFIG_WAKELOCK_STAT  
        if (lock->stat.count) {  
            deleted_wake_locks.stat.count += lock->stat.count;  
            deleted_wake_locks.stat.expire_count += lock->stat.expire_count;  
            deleted_wake_locks.stat.total_time =  
                ktime_add(deleted_wake_locks.stat.total_time,  
                      lock->stat.total_time);  
            deleted_wake_locks.stat.prevent_suspend_time =  
                ktime_add(deleted_wake_locks.stat.prevent_suspend_time,  
                      lock->stat.prevent_suspend_time);  
            deleted_wake_locks.stat.max_time =  
                ktime_add(deleted_wake_locks.stat.max_time,  
                      lock->stat.max_time);  
        }  
    #endif  
        // 從當前鏈表中刪除  
        list_del(&lock->link);  
        spin_unlock_irqrestore(&list_lock, irqflags);  
    }  
    EXPORT_SYMBOL(wake_lock_destroy);  

該函數用于注銷wake_lock,首先清除 WAKE_LOCK_INITIALIZED 標志位,然后更新統計信息,最后將鎖從鏈表中刪除。

9、proc節點
[cpp] view plaincopy

    // 獲取鎖的剩余超時時間,通過*expire_time傳遞  
    int get_expired_time(struct wake_lock *lock, ktime_t *expire_time)  
    {  
        struct timespec ts;  
        struct timespec kt;  
        struct timespec tomono;  
        struct timespec delta;  
        unsigned long seq;  
        long timeout;  

        // 如果不是超時鎖則直接返回  
        if (!(lock->flags & WAKE_LOCK_AUTO_EXPIRE))  
            return 0;  

        do {  
            seq = read_seqbegin(&xtime_lock);  
            // 計算超時時間點與當前時間的差值  
            timeout = lock->expires - jiffies;  
            // 如果時間沒有到期,返回0  
            if (timeout > 0)  
                return 0;  
            // 獲取當前時間  
            kt = current_kernel_time();  
            tomono = wall_to_monotonic;  
        } while (read_seqretry(&xtime_lock, seq));  
        // 時間格式轉換  
        jiffies_to_timespec(-timeout, &delta);  
        // 設置timespec的成員  
        set_normalized_timespec(&ts, kt.tv_sec + tomono.tv_sec - delta.tv_sec,  
                    kt.tv_nsec + tomono.tv_nsec - delta.tv_nsec);  
        // 返回ts值  
        *expire_time = timespec_to_ktime(ts);  
        return 1;  
    }  

    // 打印出鎖的狀態信息  
    static int print_lock_stat(struct seq_file *m, struct wake_lock *lock)  
    {  
        int lock_count = lock->stat.count;  
        int expire_count = lock->stat.expire_count;  
        ktime_t active_time = ktime_set(0, 0);  
        ktime_t total_time = lock->stat.total_time;  
        ktime_t max_time = lock->stat.max_time;  

        ktime_t prevent_suspend_time = lock->stat.prevent_suspend_time;  
        // 如果鎖有效  
        if (lock->flags & WAKE_LOCK_ACTIVE) {  
            ktime_t now, add_time;  
            // 獲取超時剩余時間  
            int expired = get_expired_time(lock, &now);  
            if (!expired)  
                now = ktime_get();  
            // 計算當前時間和上次操作時間的差值  
            add_time = ktime_sub(now, lock->stat.last_time);  
            lock_count++;  // 使用計數加1  
            if (!expired)  // 如果沒有到期  
                active_time = add_time;  
            else  // 鎖已經到期  
                expire_count++;  // 超時計數加1  
            total_time = ktime_add(total_time, add_time);  // 鎖使用時間增加  
            if (lock->flags & WAKE_LOCK_PREVENTING_SUSPEND)  
                prevent_suspend_time = ktime_add(prevent_suspend_time,  
                        ktime_sub(now, last_sleep_time_update));  
            if (add_time.tv64 > max_time.tv64)  
                max_time = add_time;  
        }  

        return seq_printf(m,  
                 "\"%s\"\t%d\t%d\t%d\t%lld\t%lld\t%lld\t%lld\t%lld\n",  
                 lock->name, lock_count, expire_count,  
                 lock->stat.wakeup_count, ktime_to_ns(active_time),  
                 ktime_to_ns(total_time),  
                 ktime_to_ns(prevent_suspend_time), ktime_to_ns(max_time),  
                 ktime_to_ns(lock->stat.last_time));  
    }  

    // 打印鎖狀態  
    static int wakelock_stats_show(struct seq_file *m, void *unused)  
    {  
        unsigned long irqflags;  
        struct wake_lock *lock;  
        int ret;  
        int type;  

        spin_lock_irqsave(&list_lock, irqflags);  
        // 輸出菜單  
        ret = seq_puts(m, "name\tcount\texpire_count\twake_count\tactive_since"  
                "\ttotal_time\tsleep_time\tmax_time\tlast_change\n");  
        // 遍歷無效鎖鏈表并打印鎖的狀態信息  
        list_for_each_entry(lock, &inactive_locks, link)  
            ret = print_lock_stat(m, lock);  
        // 遍歷有效鎖鏈表并打印鎖的狀態信息  
        for (type = 0; type < WAKE_LOCK_TYPE_COUNT; type++) {  
            list_for_each_entry(lock, &active_wake_locks[type], link)  
                ret = print_lock_stat(m, lock);  
        }  
        spin_unlock_irqrestore(&list_lock, irqflags);  
        return 0;  
    }  

    // proc文件打開函數,調用show函數顯示當前所有的鎖信息  
    static int wakelock_stats_open(struct inode *inode, struct file *file)  
    {  
        return single_open(file, wakelock_stats_show, NULL);  
    }  

    // proc文件系統操作函數  
    static const struct file_operations wakelock_stats_fops = {  
        .owner = THIS_MODULE,  
        .open = wakelock_stats_open,  
        .read = seq_read,  
        .llseek = seq_lseek,  
        .release = single_release,  
    };  

以上是proc節點的操作接口,在wakelocks_init中注冊。


總結:通過以上分析我們可以看到啟動深度休眠流程有四個可能的地方,分別為expire_timer、wake_lock、wake_lock_timeout、wake_unlock,其中expire_timer和wake_unlock最常見。
分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

沙發
ID:71922 發表于 2015-1-10 19:08 | 只看該作者
early_suspend是Android休眠流程的第一階段即淺度休眠,不會受到wake_lock的阻止,一般用于關閉lcd、tp等設備為運行的應用節約電能。Android的PowerManagerService會根據用戶的操作情況調整電源狀態,如果需要休眠則會調用到HAL層的set_screen_state()接口,在set_screen_state()中會向/sys/power/state節點寫入"mem"值讓驅動層開始進入休眠流程。

一、休眠喚醒機制及其用戶空間接口

Linux系統支持如下休眠喚醒等級
[cpp] view plaincopy

    const char *const pm_states[PM_SUSPEND_MAX] = {  
    #ifdef CONFIG_EARLYSUSPEND  
        [PM_SUSPEND_ON]     = "on",  
    #endif  
        [PM_SUSPEND_STANDBY]    = "standby",  
        [PM_SUSPEND_MEM]    = "mem",  
    };  

但在Android中一般只支持"on"和"mem",其中"on"為喚醒設備,"mem"為休眠設備。/sys/power/state節點的讀寫操作如下:
[cpp] view plaincopy

    static ssize_t state_show(struct kobject *kobj, struct kobj_attribute *attr,  
                  char *buf)  
    {  
        char *s = buf;  
    #ifdef CONFIG_SUSPEND  
        int i;  
      
        for (i = 0; i < PM_SUSPEND_MAX; i++) {  
            if (pm_states[ i] && valid_state(i))  
                s += sprintf(s,"%s ", pm_states[ i]);  // 打印系統支持的休眠等級  
        }  
    #endif  
    #ifdef CONFIG_HIBERNATION  
        s += sprintf(s, "%s\n", "disk");  
    #else  
        if (s != buf)  
            /* convert the last space to a newline */  
            *(s-1) = '\n';  
    #endif  
        return (s - buf);  
    }  
      
    static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,  
                   const char *buf, size_t n)  
    {  
    #ifdef CONFIG_SUSPEND  
    #ifdef CONFIG_EARLYSUSPEND  
        suspend_state_t state = PM_SUSPEND_ON;  
    #else  
        suspend_state_t state = PM_SUSPEND_STANDBY;  
    #endif  
        const char * const *s;  
    #endif  
        char *p;  
        int len;  
        int error = -EINVAL;  
      
        p = memchr(buf, '\n', n);  
        len = p ? p - buf : n;  
      
        /* First, check if we are requested to hibernate */  
        if (len == 4 && !strncmp(buf, "disk", len)) {  
            error = hibernate();  
      goto Exit;  
        }  
      
    #ifdef CONFIG_SUSPEND  
        for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {  
            if (*s && len == strlen(*s) && !strncmp(buf, *s, len))  
                break;  
        }  
        if (state < PM_SUSPEND_MAX && *s)  
    #ifdef CONFIG_EARLYSUSPEND  
            if (state == PM_SUSPEND_ON || valid_state(state)) {  
                error = 0;  
                request_suspend_state(state);  // 請求進入android的休眠流程  
            }  
    #else  
            error = enter_state(state);  // linux的標準休眠流程  
    #endif  
    #endif  
      
     Exit:  
        return error ? error : n;  
    }  
      
    power_attr(state);  

其中state_show()為節點的讀函數,主要打印出系統支持的休眠等級;state_store()為節點的寫函數,根據參數請求休眠或者喚醒流程。節點的創建代碼如下:

[cpp] view plaincopy

    static struct attribute * g[] = {  
        &state_attr.attr,        // state節點  
    #ifdef CONFIG_PM_TRACE  
        &pm_trace_attr.attr,  
    #endif  
    #if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_DEBUG)  
        &pm_test_attr.attr,      // pm_test節點  
    #endif  
    #ifdef CONFIG_USER_WAKELOCK  
        &wake_lock_attr.attr,    // wake_lock節點  
        &wake_unlock_attr.attr,  // wake_unlock節點  
    #endif  
        NULL,  
    };  
      
    static struct attribute_group attr_group = {  
        .attrs = g,  
    };  
      
    static int __init pm_init(void)  
    {  
        int error = pm_start_workqueue();  
        if (error)  
            return error;  
        power_kobj = kobject_create_and_add("power", NULL);  // 創建power節點  
        if (!power_kobj)  
            return -ENOMEM;  
        return sysfs_create_group(power_kobj, &attr_group);  // 創建一組屬性節點  
    }  
      
    core_initcall(pm_init);  

二、early_suspend 實現

1、early_suspend 定義、接口及其用法

[cpp] view plaincopy

    enum {  
        EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,  
        EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,  
        EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,  
    };  
    struct early_suspend {  
    #ifdef CONFIG_HAS_EARLYSUSPEND  
        struct list_head link;  // 鏈表節點  
        int level;              // 優先等級  
        void (*suspend)(struct early_suspend *h);  
        void (*resume)(struct early_suspend *h);  
    #endif  
    };  

可以看到early_suspend由兩個函數指針、鏈表節點、優先等級組成;內核默認定義了3個優先等級,在suspend的時候先執行優先等級低的handler,在resume的時候則先執行等級高的handler,用戶可以定義自己的優先等級;early_suspend向內核空間提供了2個接口用于注冊和注銷handler:

[cpp] view plaincopy

    void register_early_suspend(struct early_suspend *handler);  
    void unregister_early_suspend(struct early_suspend *handler);  

其中register_early_suspend()用于注冊,unregister_early_suspend用于注銷;一般early_suspend的使用方式如下:

[cpp] view plaincopy

    ts->earlysuspend.suspend = sitronix_i2c_suspend_early;  
    ts->earlysuspend.resume = sitronix_i2c_resume_late;  
    ts->earlysuspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;  
    register_early_suspend(&ts->earlysuspend);  

設置好suspend和resume接口,定義優先等級,然后注冊結構即可。

2、初始化信息

我們看一下early_suspend需要用到的一些數據:

[cpp] view plaincopy

    static DEFINE_MUTEX(early_suspend_lock);  
    static LIST_HEAD(early_suspend_handlers);  // 初始化淺度休眠鏈表  
    // 聲明3個工作隊列用于同步、淺度休眠和喚醒  
    static void early_sys_sync(struct work_struct *work);  
    static void early_suspend(struct work_struct *work);  
    static void late_resume(struct work_struct *work);  
    static DECLARE_WORK(early_sys_sync_work,early_sys_sync);  
    static DECLARE_WORK(early_suspend_work, early_suspend);  
    static DECLARE_WORK(late_resume_work, late_resume);  
    static DEFINE_SPINLOCK(state_lock);  
    enum {  
        SUSPEND_REQUESTED = 0x1,  // 當前正在請求淺度休眠  
        SUSPENDED = 0x2,          // 淺度休眠完成  
        SUSPEND_REQUESTED_AND_SUSPENDED = SUSPEND_REQUESTED | SUSPENDED,  
    };  
    static int state;  

初始化了一個鏈表early_suspend_handlers用于管理early_suspend,還定義讀寫鏈表用到的互斥體;另外還聲明了3個工作隊列,分別用于緩存同步、淺度休眠和喚醒;還聲明了early_suspend操作的3個狀態。
3、register_early_suspend 和 unregister_early_suspend

[cpp] view plaincopy

    void register_early_suspend(struct early_suspend *handler)  
    {  
        struct list_head *pos;  
      
        mutex_lock(&early_suspend_lock);  
        // 遍歷淺度休眠鏈表  
        list_for_each(pos, &early_suspend_handlers) {  
            struct early_suspend *e;  
            e = list_entry(pos, struct early_suspend, link);  
            // 判斷當前節點的優先等級是否大于handler的優先等級  
            // 以此決定handler在鏈表中的順序  
            if (e->level > handler->level)  
                break;  
        }  
        // 將handler加入當前節點之前,優先等級越低越靠前  
        list_add_tail(&handler->link, pos);  
        if ((state & SUSPENDED) && handler->suspend)  
            handler->suspend(handler);  
        mutex_unlock(&early_suspend_lock);  
    }  
    EXPORT_SYMBOL(register_early_suspend);  

注冊的流程比較簡單,首先遍歷鏈表,依次比較每個節點的優先等級,如果遇到優先等級比新節點優先等級高則跳出,然后將新節點加入優先等級較高的節點前面,這樣就確保了鏈表是優先等級低在前高在后的順序;在將節點加入鏈表后查看當前狀態是否為淺度休眠完成狀態,如果是則執行handler的suspend函數。

[cpp] view plaincopy

    void unregister_early_suspend(struct early_suspend *handler)  
    {  
        mutex_lock(&early_suspend_lock);  
        list_del(&handler->link);  
        mutex_unlock(&early_suspend_lock);  
    }  
    EXPORT_SYMBOL(unregister_early_suspend);  

注銷流程則只是將節點從鏈表中移除。
4、request_suspend_state

前面我們看到用戶空間在寫/sys/power/state節點的時候會執行request_suspend_state()函數,該函數代碼如下:

[cpp] view plaincopy

    void request_suspend_state(suspend_state_t new_state)  
    {  
        unsigned long irqflags;  
        int old_sleep;  
      
        spin_lock_irqsave(&state_lock, irqflags);  
        old_sleep = state & SUSPEND_REQUESTED;  
        // 打印當前狀態  
        if (debug_mask & DEBUG_USER_STATE) {  
            struct timespec ts;  
            struct rtc_time tm;  
            getnstimeofday(&ts);  
            rtc_time_to_tm(ts.tv_sec, &tm);  
            pr_info("request_suspend_state: %s (%d->%d) at %lld "  
                "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",  
                new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",  
                requested_suspend_state, new_state,  
                ktime_to_ns(ktime_get()),  
                tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,  
                tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);  
        }  
        // 如果新狀態是休眠狀態  
        if (!old_sleep && new_state != PM_SUSPEND_ON) {  
            state |= SUSPEND_REQUESTED;  
            pr_info("sys_sync_work_queue early_sys_sync_work.\n");  
            // 執行緩存同步與淺度休眠的工作隊列  
            queue_work(sys_sync_work_queue, &early_sys_sync_work);  
            queue_work(suspend_work_queue, &early_suspend_work);  
        } else if (old_sleep && new_state == PM_SUSPEND_ON) {  
        // 如果新狀態是喚醒狀態  
            state &= ~SUSPEND_REQUESTED;  
            // 激活內核鎖  
            wake_lock(&main_wake_lock);  
            // 執行淺度喚醒的工作隊列  
            queue_work(suspend_work_queue, &late_resume_work);  
        }  
        // 更新全局狀態  
        requested_suspend_state = new_state;  
        spin_unlock_irqrestore(&state_lock, irqflags);  
    }  

函數首先打印出當前狀態變化的log,然后判斷新狀態,如果是休眠狀態則置位SUSPEND_REQUESTED標志,然后將同步緩存、淺度休眠工作隊列加入相應的內核線程執行;如果新狀態是喚醒則首先將main_wake_lock激活,然后再將淺度喚醒工作隊列加入內核線程執行;最后更新全局狀態變量,因為提供了一個內核空間接口用于獲取當前休眠喚醒狀態:

[cpp] view plaincopy

    // 返回系統狀態值  
    suspend_state_t get_suspend_state(void)  
    {  
        return requested_suspend_state;  
    }  

5、early_suspend_work、late_resume_work 和 early_sys_sync
[cpp] view plaincopy

    static void early_suspend(struct work_struct *work)  
    {  
        struct early_suspend *pos;  
        unsigned long irqflags;  
        int abort = 0;  
      
        mutex_lock(&early_suspend_lock);  
        spin_lock_irqsave(&state_lock, irqflags);  
        if (state == SUSPEND_REQUESTED)  // 判斷當前狀態是否在請求淺度休眠  
            state |= SUSPENDED;      // 如果是則置位SUSPENDED  
        else  
            abort = 1;  
        spin_unlock_irqrestore(&state_lock, irqflags);  
      
        if (abort) {  // 取消early_suspend  
            if (debug_mask & DEBUG_SUSPEND)  
                pr_info("early_suspend: abort, state %d\n", state);  
            mutex_unlock(&early_suspend_lock);  
            goto abort;  
        }  
      
        if (debug_mask & DEBUG_SUSPEND)  
            pr_info("early_suspend: call handlers\n");  
        // 遍歷淺度休眠鏈表并執行其中所有suspend函數  
        // 執行順序根據優先等級而定,等級越低越先執行  
        list_for_each_entry(pos, &early_suspend_handlers, link) {  
            if (pos->suspend != NULL)  
                pos->suspend(pos);  
        }  
        mutex_unlock(&early_suspend_lock);  
      
        if (debug_mask & DEBUG_SUSPEND)  
            pr_info("early_suspend: sync\n");  
      
        /* Remove sys_sync from early_suspend, and use work queue to complete sys_sync */  
        //sys_sync();  
    abort:  
        spin_lock_irqsave(&state_lock, irqflags);  
        if (state == SUSPEND_REQUESTED_AND_SUSPENDED)  
            wake_unlock(&main_wake_lock);  
        spin_unlock_irqrestore(&state_lock, irqflags);  
    }  

在suspend流程中首先判斷當前狀態是否為SUSPEND_REQUESTED,如果是則置位SUSPENDED標志,如果不是則取消suspend流程;然后遍歷淺度休眠鏈表,從鏈表頭部到尾部依次調用各節點的suspend()函數,執行完后判斷當前狀態是否為SUSPEND_REQUESTED_AND_SUSPENDED,如果是則釋放main_wake_lock,當前系統中如果只存在main_wake_lock這個有效鎖,則會在wake_unlock()里面啟動深度休眠線程,如果還有其他其他wake_lock則保持當前狀態。

[cpp] view plaincopy

    static void late_resume(struct work_struct *work)  
    {  
        struct early_suspend *pos;  
        unsigned long irqflags;  
        int abort = 0;  
      
        mutex_lock(&early_suspend_lock);  
        spin_lock_irqsave(&state_lock, irqflags);  
        if (state == SUSPENDED)  // 清除淺度休眠完成標志  
            state &= ~SUSPENDED;  
        else  
            abort = 1;  
        spin_unlock_irqrestore(&state_lock, irqflags);  
      
        if (abort) {  
            if (debug_mask & DEBUG_SUSPEND)  
                pr_info("late_resume: abort, state %d\n", state);  
            goto abort;  
        }  
        if (debug_mask & DEBUG_SUSPEND)  
            pr_info("late_resume: call handlers\n");  
        // 反向遍歷淺度休眠鏈表并執行其中所有resume函數  
        // 執行順序根據優先等級而定,等級越高越先執行  
        list_for_each_entry_reverse(pos, &early_suspend_handlers, link)  
            if (pos->resume != NULL)  
                pos->resume(pos);  
        if (debug_mask & DEBUG_SUSPEND)  
            pr_info("late_resume: done\n");  
    abort:  
        mutex_unlock(&early_suspend_lock);  
    }  

在resume流程中同樣首先判斷當前狀態是否為SUSPENDED,如果是則清除SUSPENDED標志,然后反向遍歷淺度休眠鏈表,按照優先等級從高到低的順序執行節點的resume()函數。

[cpp] view plaincopy

    static void early_sys_sync(struct work_struct *work)  
    {  
        wake_lock(&sys_sync_wake_lock);  
        sys_sync();  
        wake_unlock(&sys_sync_wake_lock);  
    }  

內核專門為緩存同步建立了一個線程,同時還創建了sys_sync_wake_lock防止在同步緩存時系統進入深度休眠。
回復

使用道具 舉報

板凳
ID:71922 發表于 2015-1-10 19:09 | 只看該作者
前面我們分析了休眠的第一個階段即淺度休眠,現在我們繼續看休眠的第二個階段 — 深度休眠。在深度休眠的過程中系統會首先凍結所有可以凍結的進程,然后依次掛起所有設備的電源,掛起順序與設備注冊的順序相反,這樣保證了設備之間電源的依賴性;直至最后進入省電模式,等待用戶或者RTC喚醒;在喚醒過程中則會按照設備注冊的順序依次恢復每個設備的電源進入正常工作狀態,解凍相關的進程,然后再進行淺度休眠的喚醒流程。

1、深度休眠入口

根據wake_lock一節的分析我們知道driver層進入深度休眠的入口有4個,分別為expire_timer、wake_lock、wake_lock_timeout、wake_unlock,這幾個入口函數將根據相應的條件啟動suspend_work里面的pm_suspend()函數進入深度休眠流程,代碼在linux/kernel/power/suspend.c中:

[cpp] view plaincopy

    // 進入深度休眠流程  
    int enter_state(suspend_state_t state)  
    {  
        int error;  
        // 判斷平臺是否支持該狀態  
        if (!valid_state(state))  
            return -ENODEV;  
      
        if (!mutex_trylock(&pm_mutex))  
            return -EBUSY;  
        // 同步緩存  
        printk(KERN_INFO "PM: Syncing filesystems ... ");  
        sys_sync();  
        printk("done.\n");  
      
        pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);  
        // 做好休眠準備  
        error = suspend_prepare();  
        if (error)  
            goto Unlock;  
        // suspend_test  
        if (suspend_test(TEST_FREEZER))  
            goto Finish;  
      
        pr_debug("PM: Entering %s sleep\n", pm_states[state]);  
        // 設備休眠  
        error = suspend_devices_and_enter(state);  
      
     Finish:  
        pr_debug("PM: Finishing wakeup.\n");  
        suspend_finish();  
     Unlock:  
        mutex_unlock(&pm_mutex);  
        return error;  
    }  
      
    int pm_suspend(suspend_state_t state)  
    {  
        if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)  
            return enter_state(state);  
        return -EINVAL;  
    }  
    EXPORT_SYMBOL(pm_suspend);  

在enter_state()中首先進入狀態的判斷,根據平臺的特性判斷是否支持此狀態;然后再同步緩存;接著調用suspend_prepare()凍結大部分進程;然后再通過suspend_devices_and_enter()開始掛起設備。

2、凍結進程
[cpp] view plaincopy

    static int suspend_prepare(void)  
    {  
        int error;  
      
        if (!suspend_ops || !suspend_ops->enter)  
            return -EPERM;  
      
        pm_prepare_console();  
      
        // 通知進行休眠準備  
        error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);  
        if (error)  
            goto Finish;  
        // 禁止usermodehelper  
        error = usermodehelper_disable();  
        if (error)  
            goto Finish;  
        // 凍結所有可以凍結的進程  
        error = suspend_freeze_processes();  
        if (!error)  
            return 0;  
      
        // 解凍所有進程  
        suspend_thaw_processes();  
        // 使能usermodehelper  
        usermodehelper_enable();  
     Finish:  
        // 通知休眠結束  
        pm_notifier_call_chain(PM_POST_SUSPEND);  
        pm_restore_console();  
        return error;  
    }  

這里有一個notifier機制后面要專門分析下。

3、掛起設備

[cpp] view plaincopy

    int suspend_devices_and_enter(suspend_state_t state)  
    {  
        int error;  
      
        if (!suspend_ops)  
            return -ENOSYS;  
        // 處理器的休眠開始函數  
        if (suspend_ops->begin) {  
            error = suspend_ops->begin(state);  
            if (error)  
                goto Close;  
        }  
        // 休眠串口  
        suspend_console();  
        suspend_test_start();  
        // 設備休眠  
        error = dpm_suspend_start(PMSG_SUSPEND);  
        if (error) {  
            printk(KERN_ERR "PM: Some devices failed to suspend\n");  
            goto Recover_platform;  
        }  
        suspend_test_finish("suspend devices");  
        if (suspend_test(TEST_DEVICES))  
            goto Recover_platform;  
        // 處理器休眠  
        suspend_enter(state);  
      
     Resume_devices:  
        suspend_test_start();  
        // 設備喚醒  
        dpm_resume_end(PMSG_RESUME);  
        suspend_test_finish("resume devices");  
        // 喚醒串口  
        resume_console();  
     Close:  
        // 處理器的休眠結束函數  
        if (suspend_ops->end)  
            suspend_ops->end();  
        return error;  
      
     Recover_platform:  
        if (suspend_ops->recover)  
            suspend_ops->recover();  
        goto Resume_devices;  
    }  

可以看到設備掛起流程先從處理器自身開始,平臺一般不需要做特殊的處理;接著關閉串口,然后調用dpm_suspend_start()開始掛起設備,如果成功掛起所有設備則調用suspend_enter()掛起處理器。掛起設備部分的代碼在linux/driver/base/power/main.c中

[cpp] view plaincopy

    int dpm_suspend_start(pm_message_t state)  
    {  
        int error;  
      
        might_sleep();  
        error = dpm_prepare(state);  
        if (!error)  
            error = dpm_suspend(state);  
        return error;  
    }  
    EXPORT_SYMBOL_GPL(dpm_suspend_start);  

掛起設備分為2個步驟,首先執行設備的prepare函數,然后再執行suspend函數。
[cpp] view plaincopy

    // 函數將會調用所有的非sysdev設備的prepare()接口  
    static int dpm_prepare(pm_message_t state)  
    {  
        struct list_head list;  
        int error = 0;  
      
        INIT_LIST_HEAD(&list);  
        mutex_lock(&dpm_list_mtx);  
        transition_started = true;  
        // 遍歷設備鏈表  
        while (!list_empty(&dpm_list)) {  
            // 從最先初始化的節點開始遍歷  
            struct device *dev = to_device(dpm_list.next);  
            // 獲取設備  
            get_device(dev);  
            // 更新設備狀態  
            dev->power.status = DPM_PREPARING;  
            mutex_unlock(&dpm_list_mtx);  
      
            pm_runtime_get_noresume(dev);  
            // 在系統休眠期間有可能受到喚醒請求  
            if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) {  
                /* Wake-up requested during system sleep transition. */  
                pm_runtime_put_noidle(dev);  
                error = -EBUSY;  
            } else {  // 執行prepare()函數  
                error = device_prepare(dev, state);  
            }  
      
            mutex_lock(&dpm_list_mtx);  
            // 如果出錯則跳出循環  
            if (error) {  
                dev->power.status = DPM_ON;  
                if (error == -EAGAIN) {  
                    put_device(dev);  
                    error = 0;  
                    continue;  
                }  
                printk(KERN_ERR "PM: Failed to prepare device %s "  
                    "for power transition: error %d\n",  
                    kobject_name(&dev->kobj), error);  
                put_device(dev);  
                break;  
            }  
            // 更新狀態  
            dev->power.status = DPM_SUSPENDING;  
            if (!list_empty(&dev->power.entry))  
                // 將設備節點移動到list鏈表中  
                list_move_tail(&dev->power.entry, &list);  
            put_device(dev);  
        }  
        // 拼接鏈表  
        list_splice(&list, &dpm_list);  
        mutex_unlock(&dpm_list_mtx);  
        return error;  
    }  

可以看到函數將遍歷dpm_list鏈表,并執行每個設備的prepare函數,內核規定prepare函數的實現不能改變硬件的狀態;系統中每一個設備注冊時都將被加入dpm_list鏈表的尾部,所以鏈表排序為設備注冊的順序。
[cpp] view plaincopy

    static int dpm_suspend(pm_message_t state)  
    {  
        struct list_head list;  
        int error = 0;  
      
        INIT_LIST_HEAD(&list);  
        mutex_lock(&dpm_list_mtx);  
        while (!list_empty(&dpm_list)) {  
            // 逆序遍歷鏈表,即先suspend后注冊的設備,符合設備與父設備電源掛起的先后原則  
            struct device *dev = to_device(dpm_list.prev);  
      
            get_device(dev);  
            mutex_unlock(&dpm_list_mtx);  
      
            dpm_drv_wdset(dev);  
            error = device_suspend(dev, state);  
            dpm_drv_wdclr(dev);  
      
            mutex_lock(&dpm_list_mtx);  
            if (error) {  
                pm_dev_err(dev, state, "", error);  
                put_device(dev);  
                break;  
            }  
            dev->power.status = DPM_OFF;  
            if (!list_empty(&dev->power.entry))  
                list_move(&dev->power.entry, &list);  
            put_device(dev);  
        }  
        list_splice(&list, dpm_list.prev);  
        mutex_unlock(&dpm_list_mtx);  
        return error;  
    }  

函數將設備按照注冊順序反向掛起,掛起執行的流程如下:
[cpp] view plaincopy

    static int device_suspend(struct device *dev, pm_message_t state)  
    {  
        int error = 0;  
      
        down(&dev->sem);  
      
        if (dev->class) {  // 類的suspend優先  
            if (dev->class->pm) {  
                pm_dev_dbg(dev, state, "class ");  
                error = pm_op(dev, dev->class->pm, state);  
            } else if (dev->class->suspend) {  
                pm_dev_dbg(dev, state, "legacy class ");  
                error = dev->class->suspend(dev, state);  
                suspend_report_result(dev->class->suspend, error);  
            }  
            if (error)  
                goto End;  
        }  
      
        if (dev->type) {  // device_type次之  
            if (dev->type->pm) {  
                pm_dev_dbg(dev, state, "type ");  
                error = pm_op(dev, dev->type->pm, state);  
            }  
            if (error)  
                goto End;  
        }  
      
        if (dev->bus) {  // bus優先級最低  
            if (dev->bus->pm) {  
                pm_dev_dbg(dev, state, "");  
                error = pm_op(dev, dev->bus->pm, state);  
            } else if (dev->bus->suspend) {  
                pm_dev_dbg(dev, state, "legacy ");  
                error = dev->bus->suspend(dev, state);  
                suspend_report_result(dev->bus->suspend, error);  
            }  
        }  
     End:  
        up(&dev->sem);  
      
        return error;  
    }  

可以看到類中的suspend優先級最高,之后是device_type的,最后是bus的,大部分設備只注冊了bus下的suspend。
4、掛起處理器

[cpp] view plaincopy

    static int suspend_enter(suspend_state_t state)  
    {  
        int error;  
        // 處理器的休眠準備函數  
        if (suspend_ops->prepare) {  
            error = suspend_ops->prepare();  
            if (error)  
                return error;  
        }  
        // 執行非sysdev的late suspend函數  
        error = dpm_suspend_noirq(PMSG_SUSPEND);  
        if (error) {  
            printk(KERN_ERR "PM: Some devices failed to power down\n");  
            goto Platfrom_finish;  
        }  
        // 處理器休眠最后的準備  
        if (suspend_ops->prepare_late) {  
            error = suspend_ops->prepare_late();  
            if (error)  
                goto Power_up_devices;  
        }  
      
        if (suspend_test(TEST_PLATFORM))  
            goto Platform_wake;  
        // 關閉非啟動cpu  
        error = disable_nonboot_cpus();  
        if (error || suspend_test(TEST_CPUS))  
            goto Enable_cpus;  
        // 掛起中斷  
        arch_suspend_disable_irqs();  
        BUG_ON(!irqs_disabled());  
        // 掛起sysdev  
        error = sysdev_suspend(PMSG_SUSPEND);  
        if (!error) {  
            if (!suspend_test(TEST_CORE))  
                // 處理器的休眠進入函數,休眠流程運行至此  
                error = suspend_ops->enter(state);  
            // 喚醒sysdev  
            sysdev_resume();  
        }  
        // 使能中斷  
        arch_suspend_enable_irqs();  
        BUG_ON(irqs_disabled());  
      
     Enable_cpus:  
        // 使能非啟動cpu  
        enable_nonboot_cpus();  
      
     Platform_wake:  
        // 處理器開始喚醒  
        if (suspend_ops->wake)  
            suspend_ops->wake();  
      
     Power_up_devices:  
        // 執行非sysdev的early resume函數  
        dpm_resume_noirq(PMSG_RESUME);  
      
     Platfrom_finish:  
        // 處理器休眠結束  
        if (suspend_ops->finish)  
            suspend_ops->finish();  
      
        return error;  
    }  

在這個階段首先看處理器是否需要做一些準備,接下來執行非sysdev的late suspend函數,然后處理器做休眠前最后的準備、關閉非啟動cpu、掛起中斷,再掛起sysdev,最后進入處理器的掛起函數,至此休眠流程結束,處理器等待用戶或者RTC喚醒。

附1、late suspend

在這里我們看到了一種新的suspend機制 — late suspend,是在所有的suspend執行完后再開始執行,接口為dev->bus->pm->suspend_noirq;這樣early_suspend、suspend以及late suspend構成了suspend的三部曲,late suspend是在中斷關閉的情況下進行的;前面我們分析的wake_lock就有用到,用于檢測在suspend階段是否有鎖被激活。late suspend的實現如下:

[cpp] view plaincopy

    int dpm_suspend_noirq(pm_message_t state)  
    {  
        struct device *dev;  
        int error = 0;  
      
        suspend_device_irqs();  // 關閉除喚醒系統以外的所有中斷  
        mutex_lock(&dpm_list_mtx);  
        list_for_each_entry_reverse(dev, &dpm_list, power.entry) {  
            // 執行所有設備的late suspend函數  
            error = device_suspend_noirq(dev, state);  
            if (error) {  
                pm_dev_err(dev, state, " late", error);  
                break;  
            }  
            dev->power.status = DPM_OFF_IRQ;  
        }  
        mutex_unlock(&dpm_list_mtx);  
        if (error)  
            dpm_resume_noirq(resume_event(state));  
        return error;  
    }  
    EXPORT_SYMBOL_GPL(dpm_suspend_noirq);  

附2、中斷關閉流程
在late suspend機制中我們看到了休眠流程中關閉系統中斷的地方:

[cpp] view plaincopy

    void suspend_device_irqs(void)  
    {  
        struct irq_desc *desc;  
        int irq;  
      
        for_each_irq_desc(irq, desc) {  // 遍歷系統的中斷  
            unsigned long flags;  
      
            spin_lock_irqsave(&desc->lock, flags);  
            __disable_irq(desc, irq, true);  // 關閉中斷  
            spin_unlock_irqrestore(&desc->lock, flags);  
        }  
      
        for_each_irq_desc(irq, desc)  
            if (desc->status & IRQ_SUSPENDED)  
                synchronize_irq(irq);  
    }  
    EXPORT_SYMBOL_GPL(suspend_device_irqs);  

函數調用了__disable_irq()來關閉中斷,我們看一下這個函數的實現:

[cpp] view plaincopy

    void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)  
    {  
        if (suspend) {  
            // 如果中斷沒有被激活或者中斷的IRQF_TIMER標志被置位則不關閉中斷  
            // 在以后的內核版本中這個標志位被換成了IRQF_NO_SUSPEND  
            // 新版的IRQF_TIMER = (__IRQF_TIMER | IRQF_NO_SUSPEND)  
            if (!desc->action || (desc->action->flags & IRQF_TIMER))  
                return;  
            desc->status |= IRQ_SUSPENDED;  
        }  
        // 判斷中斷是否被打開  
        if (!desc->depth++) {  
            // 更新標志位  
            desc->status |= IRQ_DISABLED;  
            // 關閉中斷  
            desc->chip->disable(irq);  
        }  
    }  

可以看到如果該中斷沒有被激活或者中斷的IRQF_TIMER標志被置位就不會關閉中斷,在新的內核版本中增加了專門的 IRQF_NO_SUSPEND 標志位,用來置位在休眠狀態下喚醒系統的中斷,如RTC、按鍵等;如果是其他中斷則將打開的中斷關閉掉。

附3、dpm_list鏈表

dpm_list是內核中用于設備電源管理的鏈表,設備注冊時通過一系列的調用 device_register() -> device_add() -> device_pm_add() 最后在device_pm_add()中將設備加入dpm_list鏈表中:

[cpp] view plaincopy

    // 設備創建時都會調用的函數,將設備加入dpm_list鏈表  
    void device_pm_add(struct device *dev)  
    {  
        pr_debug("PM: Adding info for %s:%s\n",  
             dev->bus ? dev->bus->name : "No Bus",  
             kobject_name(&dev->kobj));  
        mutex_lock(&dpm_list_mtx);  
        if (dev->parent) {  
            if (dev->parent->power.status >= DPM_SUSPENDING)  
                dev_warn(dev, "parent %s should not be sleeping\n",  
                     dev_name(dev->parent));  
        } else if (transition_started) {  
            /*
             * We refuse to register parentless devices while a PM
             * transition is in progress in order to avoid leaving them
             * unhandled down the road
             */  
            dev_WARN(dev, "Parentless device registered during a PM transaction\n");  
        }  
        // 將設備節點添加到鏈表尾部,即設備按注冊的先后順序從鏈表頭部到尾部  
        list_add_tail(&dev->power.entry, &dpm_list);  
        mutex_unlock(&dpm_list_mtx);  
    }  

而設備注銷的時候會調用device_pm_remove()將設備從dpm_list鏈表中移除:

[cpp] view plaincopy

    // 設備注銷時都會調用的函數,將設備從dpm_list鏈表中移除  
    void device_pm_remove(struct device *dev)  
    {  
        pr_debug("PM: Removing info for %s:%s\n",  
             dev->bus ? dev->bus->name : "No Bus",  
             kobject_name(&dev->kobj));  
        mutex_lock(&dpm_list_mtx);  
        list_del_init(&dev->power.entry);  
        mutex_unlock(&dpm_list_mtx);  
        pm_runtime_remove(dev);  
    }  
回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術交流QQ群281945664

Powered by 單片機教程網

快速回復 返回頂部 返回列表
主站蜘蛛池模板: 九九热在线精品视频 | 成人福利网 | 久久成人国产精品 | 黄色在线免费观看 | 久久69精品久久久久久久电影好 | 黄色一级大片在线免费看产 | 精品成人佐山爱一区二区 | 亚洲欧美日韩系列 | 久久综合av | 一级国产精品一级国产精品片 | 久精品久久 | 久久精品一级 | 四虎永久免费地址 | 欧美一二三 | 国产精品国产自产拍高清 | 欧美91| 欧美美女二区 | 综合精品| 国产区精品 | 亚洲九色 | 五月婷婷亚洲 | 国产精品免费一区二区三区四区 | 欧美精品在欧美一区二区 | 国产一区二区a | 日日噜噜噜夜夜爽爽狠狠视频97 | 欧美午夜在线 | 草草视频在线免费观看 | 久久乐国产精品 | www.久| 欧美日韩在线一区二区 | 欧美亚洲国产一区二区三区 | 成人在线精品视频 | 亚洲精品影院 | 天天躁日日躁狠狠的躁天龙影院 | 欧美1区2区| 亚洲综合第一页 | 狠狠干影院 | 亚洲精品永久免费 | 国产一区二区久久久 | 国产中文视频 | 欧美日韩一二三区 |