实例分析
最近博主遇到 i2c 传输慢和中断触发慢的问题,一般这种【慢】的情况大都和【性能与功耗冲突】相关,研究了 Qos 系统,打了笔 patch 解决了。
中断触发慢:注册的下降沿中断,从下降沿打到芯片中,到跑到中断处理函数,快则 270us,慢则 2.7ms。由于所做功能对中断处理时间有要求,因此要解决中断处理慢的问题。
抓 trace 分析
使用上次博主发的脚本,可以抓到 ftrace,这个脚本中博主使能了 sched_switch、sched_wakeup、irq、irq_handler_entry、irq_handler_exit、cpu_idle、pm_qos_update_request 等 event。这些 event 可以记录下 CPU 调度和中断处理情况。
从抓到的 trace 分析,中断处理慢并不是由于 CPU loading 重导致的处理不及时,而是中断来的时候,CPU0 处于 idle 状态,而 kernel-5.10 以后除了特定的 feature,所有的中断都默认发到 CPU0,这样即便设置了中断可以唤醒系统,把 CPU0 从 idle 转为 active 也要 1ms。
问题确定后,就是如何处理的问题了。找了低功耗的同事,确认 CPU 在没事情做的时候就是会进入 idle,即便在游戏场景,也不会禁止 CPU 进入 idle。
研究了一下 Linux 电源管理子系统,发现 Qos 有接口可以使用:在某一段时间内拉 Qos,可以让 CPU 在这段时间不进入 idle,使用完毕再去掉 Qos,让 CPU 可以进入 idle,这样满足了性能需求,带来的功耗也不是特别高。
PM QoS classes framework 位于 kernel/power/qos.c 中,负责系统级别的 PM QoS 管理。per-device PM QoS framework 位于 drivers/base/power/qos.c 中,负责 per-device 的 PM QoS 管理。Common header 位于 include/linux/pm_qos.h 中,负责通用数据结构的抽象、函数声明等工作。
在 kernel/power/qos.c 中,有 cpu_latency_qos_update_request 接口可以使用,通过该接口将 Qos 拉到 150,使用完毕再将 Qos 拉到 -1(关闭)。
使用方法:
1、文件开头注册自己的结构体:struct pm_qos_request my_qos_request;
2、自己驱动的 probe 函数加上:cpu_latency_qos_add_request(my_qos_request, PM_QOS_DEFAULT_VALUE);
3、在做事情前加上:cpu_latency_qos_update_request(my_qos_request, 150);
4、在做事情后加上:cpu_latency_qos_update_request(my_qos_request, PM_QOS_DEFAULT_VALUE);PM_QOS_DEFAULT_VALUE 其实就是 -1
这样在自己做事情期间,CPU 就不会进入 idle,自己模块的性能就会好很多。如果还要更好,可以在此期间调节 CPU 频率,但调频带来的功耗很高,需要自己评估。
该 patch 解决的问题:
1、中断处理慢,可以在第一次中断打进来后,拉 Qos,这样自己后面的几次中断处理一定会快,使用完毕后,去掉 Qos。
2、i2c 传输慢,其中一种情况是 i2c 传输完毕返回时,CPU0 进入 idle,导致 i2c 中断打不进来,这种情况,我们在调用 i2c_transfer 前后加上 cpu_latency_qos_update_request 的接口,就可以解决该问题。
全部0条评论
快来发表一下你的评论吧 !