找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 10976|回复: 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 单片机教程网

快速回复 返回顶部 返回列表