apm-emulation driver framework

apm-emulation driver framework

在suspend_prepare()里会call error = __pm_notifier_call_chain(PM_SUSPEND_PREPARE, -1, &nr_calls);

因为apm-emulation有注册关于此的callback func,所以会call到apm_suspend_notifier,在此函数里,将会发一个apm_event_t类型的event APM_USER_SUSPEND(通过queue_add_event),此时suspend_state进去SUSPEND_PENDING。然后wakeup apm_waitqueue,userspace如果有进程来read此event,将会读到此event,然后返回,userspace read系统调用会走到apm_read(),这是一个阻塞系统调用,没有event将会阻塞,有event读到event后返回。

此后apm_user suspend_state进入SUSPEND_READ状态

 

drivers/char/apm-emulation.c

复制代码
static int apm_suspend_notifier(struct notifier_block *nb,
                unsigned long event,
                void *dummy)
{
    struct apm_user *as;
    int err;
    unsigned long apm_event;

    /* short-cut emergency suspends */
    if (atomic_read(&userspace_notification_inhibit))
        return NOTIFY_DONE;

    switch (event) {
    case PM_SUSPEND_PREPARE:
    case PM_HIBERNATION_PREPARE:
        apm_event = (event == PM_SUSPEND_PREPARE) ?
            APM_USER_SUSPEND : APM_USER_HIBERNATION;
        /*
         * Queue an event to all "writer" users that we want
         * to suspend and need their ack.
         */
        mutex_lock(&state_lock);
        down_read(&user_list_lock);

        list_for_each_entry(as, &apm_user_list, list) {
            if (as->suspend_state != SUSPEND_WAIT && as->reader &&
                as->writer && as->suser) {
                as->suspend_state = SUSPEND_PENDING;
                atomic_inc(&suspend_acks_pending);
                queue_add_event(&as->queue, apm_event);
            }
        }

        up_read(&user_list_lock);
        mutex_unlock(&state_lock);
        wake_up_interruptible(&apm_waitqueue);

        /*
         * Wait for the the suspend_acks_pending variable to drop to
         * zero, meaning everybody acked the suspend event (or the
         * process was killed.)
         *
         * If the app won't answer within a short while we assume it
         * locked up and ignore it.
         */
        err = wait_event_interruptible_timeout(
            apm_suspend_waitqueue,
            atomic_read(&suspend_acks_pending) == 0,
            5*HZ);
     if(err == 0)
     {
       //timeout
     }
	/* let suspend proceed */
	if (err >= 0)
	return NOTIFY_OK;
复制代码

 userspace space进程(一班是power hal service)收到了APM_USER_SUSPEND event后,将会做一些想做的事情,然后会call APM_IOC_SUSPEND ioctl:

复制代码
static long
apm_ioctl(struct file *filp, u_int cmd, u_long arg)
{
    struct apm_user *as = filp->private_data;
    int err = -EINVAL;

    if (!as->suser || !as->writer)
        return -EPERM;

    switch (cmd) {
    case APM_IOC_SUSPEND:
        mutex_lock(&state_lock);

        as->suspend_result = -EINTR;

        switch (as->suspend_state) {
        case SUSPEND_READ:
            /*
             * If we read a suspend command from /dev/apm_bios,
             * then the corresponding APM_IOC_SUSPEND ioctl is
             * interpreted as an acknowledge.
             */
            as->suspend_state = SUSPEND_ACKED;
            atomic_dec(&suspend_acks_pending);
            mutex_unlock(&state_lock);

            /*
             * suspend_acks_pending changed, the notifier needs to
             * be woken up for this
             */
            wake_up(&apm_suspend_waitqueue);

            /*
             * Wait for the suspend/resume to complete.  If there
             * are pending acknowledges, we wait here for them.
             * wait_event_freezable() is interruptible and pending
             * signal can cause busy looping.  We aren't doing
             * anything critical, chill a bit on each iteration.
             */
            while (wait_event_freezable(apm_suspend_waitqueue,
                    as->suspend_state != SUSPEND_ACKED))
                msleep(10);
            break;
复制代码

 

上面会将apm_user suspend_state设置为SUSPEND_ACKED,并将suspend_acks_pending原子变量减1,然后唤醒apm_suspend_waitqueue,这个将唤醒在apm_suspend_notifier()里处理PM_SUSPEND_PREPARE时而wait的线程,这里wait的条件是suspend_acks_pending等于0。唤醒后,PM_SUSPEND_PREARE notifier call返回。

 

STR resume时,apm-emulation将收到PM_POST_SUSPEND callback,这个notifier callback是在STR resume时执行的,在suspend_finish()里callback的:

复制代码
static int apm_suspend_notifier(struct notifier_block *nb,
                unsigned long event,
                void *dummy)
    case PM_POST_SUSPEND:
    case PM_POST_HIBERNATION:
        apm_event = (event == PM_POST_SUSPEND) ?
            APM_NORMAL_RESUME : APM_HIBERNATION_RESUME;
        /*
         * Anyone on the APM queues will think we're still suspended.
         * Send a message so everyone knows we're now awake again.
         */
        queue_event(apm_event);

        /*
         * Finally, wake up anyone who is sleeping on the suspend.
         */
        mutex_lock(&state_lock);
        down_read(&user_list_lock);
        list_for_each_entry(as, &apm_user_list, list) {
            if (as->suspend_state == SUSPEND_ACKED) {
                /*
                 * TODO: maybe grab error code, needs core
                 * changes to push the error to the notifier
                 * chain (could use the second parameter if
                 * implemented)
                 */
                as->suspend_result = 0;
                as->suspend_state = SUSPEND_DONE;
            }
        }
        up_read(&user_list_lock);
        mutex_unlock(&state_lock);

        wake_up(&apm_suspend_waitqueue);
        return NOTIFY_OK;
复制代码

 上述函数会queue一个APM_NORMAL_RESUME event。

然后,当前suspend_state为SUSPEND_ACKED,然后将apm_user suspend_state改为SUSPEND_DONE,表示完整的suspend-resume流程完成了。

然后将apm_suspend_waitqueue wakeup,这个将唤醒user space的power hal service,这个service正wait在APM_IOC_SUSPEND ioctl处。然后此service将会return。

之后此service将会再去读apm_event_t,此时会读到上面的APM_NORMAL_RESUME event,读到之后,做一些想做的事情。

 

从上面所述suspend-resume完整过程来看apm_user suspend_state变化顺序为:

SUSPEND_PENDING

SUSPEND_READ

SUSPEND_ACKED

SUSPEND_DONE

 

上述分析基于kernel 4.19

 

posted @   aspirs  阅读(75)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
历史上的今天:
2020-10-14 link hub(other)
2018-10-14 双重顶(M顶)& 双重底(W底)
点击右上角即可分享
微信分享提示