https://lwn.net/Articles/732740/

cpufreq governor的作用是依照一定的策略在电源消耗和性能之间取得一定折衷。之前,通常情况下每个逻辑cpu会拥有自己独立的governor,而4.14内核的一个重要改进就是允许开发者建立全局式的governor — 只要其体系结构能够支持,不管governor跑在哪个cpu上,都允许它去修改任意一个cpu的工作频率。这将有助于开发者设计出更好的governor。

之前的governor尽管核心策略不同,但其工作机制类似:都是依靠时钟中断来驱动,每次中断发生时对当前的系统状态采样,知道cpu是否活跃、run queue上的线程数等等,然后再根据这些收集到的采样数据去预测接下来cpu的负载情况,本质上是一种时间序列分析,或者说是一种“reactive”工作模式。纯粹基于过去的表现去预测未来的行为模式,自然,这种预测的正确率没有太多保证。但是在cpu负载这个问题上,governor其实本不必如此束手束脚,改变cpu负载的那些决策动作(抢占、push task、pull task等等)都是由scheduler本身发出的,内核其实非常知道接下来即将发生什么事情,如果governor积极地和scheduler通信,就不必局限于reactive模式了。这正是本次改动的核心想法。

在4.6内核的开发周期里,Rafael Wysocki拿掉了governor对时钟中断的依赖,同时在scheduler里加了各种per-cpu的回调。当调度器执行特定事件时就触发这些回调,例如当它决定把一个新任务添加到run queue上、或者run queue上的负载发生变化了等等。传统的那些governor,例如ondemand governor和conservative governor仍然保留,尽管基础设施变了,但它们的算法没变,所以还是一种reactive式的工作模式。较新的schedutil governor就可以被视为proactive式的了,它通过当前的run queue长度计算接下来需要的工作频率,当有实时任务被调度上来时保持cpu以最大工作频率运行。

另一个与governor有关的话题是remote callbacks。目前的实现中run queue上的负载发生变化时,框架只会调用挂在自己这个run queue上的回调,有些场景下光这样是不够的,最典型的就是决定往一个remote cpu上迁移任务。此时local cpu的回调会被调用,然而这没什么用–需要改变频率的是remote cpu,不是local cpu。而等这个任务已经抵达remote cpu的run queue,触发调度事件,开始引起它本地的回调注意时,再改变频率可能已经晚了。例如在手机上,一个播放器每秒钟需要渲染60帧,也就是说,每16毫秒一帧。设想这样的一个播放器进程正在一个高频率cpu上运行,scheduler决定把它迁移到另一个正在以低频率运行的cpu上。按前边所述,对端cpu可能需要等待一个完整的时间片(4毫秒)才意识到自己应该升高频率,此时已经度过了16毫秒配额的四分之一,用户体验可能已经受到影响。

那么让governor框架直接调用对端cpu的相应回调是不是就可以了呢?这件事情还受体系结构的一些约束,对于x86来说,一个cpu若想改变其工作频率,必须写它自己的本地MSR,别的cpu没有办法去改变它。因此在x86上若要实现这个效果,就必须由回调函数或者governor框架给对端cpu发一个IPI过去,然而这种方法也有弊端,首先过于重量级,其次IPI从发射到处理也是有延迟的,同样会发生前边说的那些问题(只有一个例子,即是在一对hyperthread之间调度任务,由于硬件会保证这个core以两个hyperthread中较高的那个频率运行,因此只要local cpu保持自己的高频率不下降即可)

对于ARM,情况会好得多,ARM允许一个cpu修改系统中任意一个cpu的工作频率,这个patchset在ARM上实现了这种remote callbacks,它允许一个local cpu调用remote cpu的回调,而无需给它发一个IPI过去。4.14内核将会默认启用这个特性。