CPU省电的秘密(二):CStates

CPU省电的秘密(二):CStates

前一篇关于EIST的文章中,我们提到了CStates,这里我们详细介绍一下它的由来和运行机理。

原理篇

上一篇文章中我们介绍了EIST,OS在工作任务不重的时候,可以调用硬件提供的接口对CPU进行降频降压以节省用电。但是如果不但任务不重,而是无事可干怎么办,为什么不干脆关掉这个内核呢?

想象一下,我们新建了一个有八个大车间的新工厂,厂房干净漂亮,传送带笔直干净,各个厂房之间有宽阔的道路,到处照明充足,在车间外还有外围仓储库,和总的入厂出厂和后勤支持部门。更妙的是,这里全部机器人操作,只有你坐在总经理办公室里,手握一个可以控制各个地方的遥控器。你的最大敌人是需求极度不明确,随时都有可能变化,为了更大的利益,你会如何安排生产策略呢?聪明的你当然会在需求很大时加大马力全力生产,以求最大效益。在需求不足时,可以让有的生产线传送带减慢些,以节省不必要的消耗。需求进一步下滑时,你会停掉某个车间的流水线,同时切断流水线机器人的电源,你还留有后手,在需求回升时随时可以恢复生产。需求还是很糟糕一段时间后,你会干脆关掉这个车间,让机器人都回到仓库中,关掉车间所有的照明等等的电力,这样几乎不会消耗什么东西了,不过要恢复生产就要费一番时间了。在明确是淡季后,你关掉了几个相邻车间,并关闭其共享的道路和后勤部门,清空了附近的仓库,进一步减低消耗。在做这些事时,你需要仔细衡量各个时机,以在省电和不会被忽然来到的需求弄得焦头烂额之间寻求平衡,越节省电力,要恢复全力生产就越麻烦,而你必须做到心中有数。

这里你就是OS(操作系统),工厂是CPU,而车间就是内核(假设八核),仓库是Cache;各个省电的级别可以映射到不同的CStates(第一个调慢流水线是Pstates),你聪明的抉择自然归于OS的电源管理策略了。怎么样,是不是很简单直接呢?

历史

你也许会吃惊于这个低功耗概念是在486DX就部分引入了,远不是什么新鲜事。然而,随着时间的迁移,越来越多更加省电的状态被引入到最新的CPU中。在深度上,最开始只有C1,接着C2、C3、C5、C6、C7等等陆续得到支持。在广度上,开始时Thread级别的(HT情况下,一个Core有两个Thread),后来加入了Core级别的,在多CPU的系统上还引入的CPU整体的CStates(Package CStates)。在最深的Package CStates下的CPU,耗电几乎为0了。下面有张简表我们可以看看各个CPU的支持程度:


操作系统最开始只是简单的在Idle(空闲)时调用HLT指令从而进入C1。在Windows95开始支持ACPI后,才开始支持其他的CStates。

实现

和上一篇文章一样,我们还是要强调,OS主导了在整个CStates在的的切换,只有它最了解工作量。同时CStates要工作,硬件,固件和OS三方缺一不可。我们也就从这三个方面介绍它的工作原理。

1. 硬件

Intel CPU是CState可以工作的硬件基础。它通过一系列寄存器保证固件和OS可以得到足够的信息,有足够的手段控制CState的工作模式。下面我们详细介绍下各个不同的CStates。

· C1

所有的X86 CPU都有个HLT(”Halt”)指令,调用后,CPU会进入idle模式,什么事也不干,直到收到中断。这里是个绝佳场所可以用来节电,Intel在这里引入了C1状态,关闭了时钟信号(内部bus和APIC除外)。由于时钟信号驱动内核里绝大多数部件,它的停止,让这些设备也停止了运行,从而减少了电能消耗。有趣的是HLT指令自从8086开始就有了,这次的功能扩展几乎不要任何软件改动就能省电。

在Core 2 Duo(酷睿2)引入了C1E(Enhanced Halt),在关闭时钟的基础上调低了电压,从而更加省电。如果你在固件中打开它,HLT时会自动进入C1E而不是C1。

· C2/C3

C2也是在486DX引入,它增加了一个STPCLK(“Stop Clock”)引脚,这在某种程度上和C1很像,不同点在于,一个是软件触发的(C1),一个是硬件触发的(C2)。后来加入的关闭时钟发生器的功能让耗电进一步减少。后面C2E也出现了,C3开始内部的BUS和APIC时钟也被关闭,这里不再赘述。

要特别强调的是,C1,C2和C3下,Cache一致性是得到保证的,从而恢复现场速度也很快。

· C4/C6/C7

C4/C6/C7下,越来越多内部设备的上下文状态信息(Context)也被封存和冻结,Catch被清空和关闭。如此一来,要恢复现场变得越来越耗时。同时Cache一致性也被破坏,OS需要知道它何时需要清理Cache。

2.固件

前文中已经提到ACPI,这里不再赘述。CStates的状态转换如下图:


这里要强调一下,ACPI的C1,C2,C3并不等同于硬件提供的C1,C2,C3,C5..C7。细心的读者会发现他们个数并不一致。ACPI的CStates是个软件抽象概念,它认为进入更深的Cstate时会有更大的延时,通常延迟越大功耗对应的Cstate的功耗就越低。APCI规定C0 C1 C2需要保持cache的一致性(要保证CPU cache中的数据一定要是最新的数据),C3以及后续的state就没有这个要求了。C0和C1必须支持,而C2/C3就是可选项了。如此一来,固件就有责任将ACPI的CStates映射到合适的硬件的CStates上来。通常C0,C1,C2/C3基本一致(硬件C3和C2基本二选一),而硬件的C4到C7我们都会映射到ACPI C3上了(ACPI C3不一定唯一,后面有介绍)。

要完成基本的CStates的支持固件应该含有以下步骤:

I. 打开关闭CStates

BIOS可以通过CPUID function 5 来检查 CPU是否支持Cstate,以及支持哪些Cstate(C1 C1E C3 C6 C7),支持的最大的Cstate也可以通过MSR去设定,默认情况下增强型Cstate以及IO MWAIT Redirection是不支持的,固件要根据系统的需求决定是否开启支持该功能的寄存器。

II. 初始化

硬件提供我们两种方式进入CStates(除了C1):一个IO端口或者MWAIT指令。关于MWAIT,这里不再展开讨论,只需要知道MWAIT的方式通常延迟更小,因为不需要做耗时的IO操作。这里我们要根据用户选择或者预设,开启或者关闭MWAIT的支持。


III. 填写各种ACPI tables

1. _OSC & _P.DC

_0SC(Operating System Details) & _PDC(Processor Driver Capabilities)在功能上比较接近,基本上供OSPM调用和BIOS传递一些关于Cstate Pstate Tstate是否支持,以及支持的程度和实现方式的一些设定,BIOS可以依据OSPM的参数回报相应的ACPI Structures。

2. _CST

_CST 汇报给OSPM的有关该平台CPU所支持的C-state的信息。它的格式如下所示:

CSTPackage : Package ( Count ,  CState ,…,  CState )

其中Count表示所支持的Cstate的个数

CState: Package ( Register ,  Type ,  Latency ,  Power )

Register表示OSPM调整C-state的方式,Type表示CState的类型(1=C1, 2=C2, 3=C3)。Latency表示进入该Cstate的最大的延迟, Power表示在该Cstate时的功耗(单位是毫瓦)。下面是个样例,这个CPU支持4个Cstate,其中C1使用FFixedHW的方式访问,其它3个Cstate都是通过IO方式。

Name(_CST, Package()

{
4,
Package(){ResourceTemplate(){Register(FFixedHW, 0, 0, 0)}, 1, 20, 1000},
Package(){ResourceTemplate(){Register(SystemIO, 8, 0, 0x161)}, 2, 40, 750},
Package(){ResourceTemplate(){Register(SystemIO, 8, 0, 0x162)}, 3, 60, 500},
Package(){ResourceTemplate(){Register(SystemIO, 8, 0, 0x163)}, 3, 100, 250}
})

3. _CSD

C-State Dependency 用于向OSPM提供多个Thread(逻辑Core)之间Cstate的依赖关系。比如在一个Dual Core的平台上,每颗核可以独立运行C1但是如果其中一个核切换到C2,另一个也必须要切换到C2,这时就需要在_CSD中提供这部分信息。

上面的逻辑通过程序看可能会更加直观。大家可以访问在Github上的Minnow3项目的源程序。代码在tianocore/edk2-platforms

3.OS

OS在收集到了各个ACPI CStates的耗电和延时数据后,会综合考虑迁移到各个状态的代价,而这些是一个叫做电源策略管理器(power policy manager)的模块来管理,在用户选择了不同的运行场景(可以调用powercfg.exe查看修改)情况下,根据目前工作量来调用processr.sys,这是个通用驱动,它再调用不同的CPU厂商的特有驱动来切换,Intel的EIST驱动是intelppm.sys,

后记

现在不但各个操作系统利用CStates来省电,甚至在固件的短短运行周期里也会在内核不工作时使其进入Cstate。有兴趣的人可以查看EDKII的CPU代码,其通过一个PCD来控制Idle时是Busy Idle, HLT还是更深的Cstate。

其他CPU电源管理文章:

CPU省电的秘密(一):EIST - 知乎专栏

睿频:榨干CPU所有的潜力(CPU电源管理系列番外篇) - 知乎专栏

欢迎大家关注本专栏和用微信扫描下方二维码加入微信公众号"UEFIBlog",在那里有最新的文章。同时欢迎大家给本专栏和公众号投稿!

用微信扫描二维码加入UEFIBlog公众号
编辑于 2017-10-28

文章被以下专栏收录