1. 程式人生 > >android 休眠喚醒機制分析(一) — wake_lock【轉】

android 休眠喚醒機制分析(一) — wake_lock【轉】

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

一、wake_lock 定義和介面

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、核心空間介面

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()用於解鎖使之成為無效鎖。另外還有兩個介面:

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向用戶空間提供了兩個檔案節點用於申請鎖和解鎖:

// 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的實現程式碼,首先看看其定義的一些初始化資訊:

#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系統初始化

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初始化

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()啟用該鎖:

// 根據引數啟用鎖
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

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

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

// 移除過期超時鎖
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;
}

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

7、wake_lock_active

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

8、wake_lock_destroy

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節點

// 獲取鎖的剩餘超時時間,通過*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最常見。