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
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 浏览器原生「磁吸」效果!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底)