Intel Powerclamp 驱动程序

作者:

简介

考虑这样一种情况:由于功率预算、散热限制或噪音水平,系统在运行时必须降低功耗,并且不倾向于采用主动散热。此时必须执行软件管理的被动降功耗,以防止硬件为灾难性场景而设计的动作。

目前,P-状态、T-状态(时钟调制)和 CPU 下线用于 CPU 节流。

在 Intel CPU 上,C-状态提供了有效的功耗降低,但到目前为止,它们仅根据工作负载进行机会性使用。随着 intel_powerclamp 驱动程序的开发,引入了跨所有在线 CPU 线程同步空闲注入的方法。目标是实现强制和可控的 C-状态驻留。

已在功耗、性能、可扩展性和用户体验方面进行了测试/分析。在许多情况下,与 CPU 下线或调制 CPU 时钟相比,显示出明显的优势。

操作理论

空闲注入

在现代 Intel 处理器(Nehalem 或更新版本)上,包级 C-状态驻留可在 MSR 中获得,因此内核也能获得。

这些 MSR 是:

#define MSR_PKG_C2_RESIDENCY      0x60D
#define MSR_PKG_C3_RESIDENCY      0x3F8
#define MSR_PKG_C6_RESIDENCY      0x3F9
#define MSR_PKG_C7_RESIDENCY      0x3FA

如果内核也可以向系统注入空闲时间,那么就可以建立一个管理包级 C-状态的闭环控制系统。intel_powerclamp 驱动程序被设想为这样一个控制系统,其中目标设定点是用户选择的空闲率(基于功耗降低),误差是实际包级 C-状态驻留率与目标空闲率之间的差异。

注入由为每个在线 CPU 生成的高优先级内核线程控制。

这些具有 SCHED_FIFO 类别的内核线程被创建用于执行受控占空比和持续时间的钳制操作。每个每 CPU 线程根据 jiffies 的取整同步其空闲时间和持续时间,从而可以防止累积误差以避免抖动效应。线程也绑定到 CPU,以便它们不能迁移,除非 CPU 下线。在这种情况下,属于下线 CPU 的线程将立即终止。

以 SCHED_FIFO 运行且优先级相对较高,也使得该方案适用于可抢占和不可抢占的内核。空闲时间围绕 jiffies 对齐确保了 HZ 值的可扩展性。这种效果可以通过 Perf 时间图更好地可视化。下图显示了内核线程 kidle_inject/cpu 的行为。在空闲注入期间,它运行 monitor/mwait 空闲给定的“持续时间”,然后将 CPU 让给其他任务,直到下一个时间间隔。

在空闲期间,NOHZ 调度时钟被禁用,但中断未被屏蔽。测试表明,来自调度器时钟的额外唤醒对大型系统(具有 80 个处理器的 Westmere 系统)上 powerclamp 驱动程序的效率有巨大影响。

CPU0
                  ____________          ____________
kidle_inject/0   |   sleep    |  mwait |  sleep     |
        _________|            |________|            |_______
                               duration
CPU1
                  ____________          ____________
kidle_inject/1   |   sleep    |  mwait |  sleep     |
        _________|            |________|            |_______
                              ^
                              |
                              |
                              roundup(jiffies, interval)

只允许一个 CPU 收集统计数据和更新全局控制参数。这个 CPU 在本文档中被称为控制 CPU。控制 CPU 在运行时选举,策略偏向 BSP,并考虑了 CPU 热插拔的可能性。

就空闲控制系统的动态而言,包级空闲时间在很大程度上被认为是一个非因果系统,其行为不能基于过去或当前的输入。因此,intel_powerclamp 驱动程序试图根据给定输入(目标空闲率)立即强制执行所需的空闲时间。注入后,powerclamp 在给定时间窗口内监控实际空闲情况,并相应调整下一次注入,以避免过量/不足校正。

当用于因果控制系统(如温度控制)时,该驱动程序的用户需要实现将过去样本和输出纳入反馈的算法。例如,基于 PID 的温度控制器可以使用 powerclamp 驱动程序,根据过去样本的积分和微分增益来维持所需的目标温度。

校准

在可扩展性测试期间,观察到随着核心数量的增加,CPU 之间的同步操作变得具有挑战性。系统进入包级 C 状态的能力也是如此。

为了确保 intel_powerclamp 驱动程序能够很好地扩展,实现了在线校准。进行此类校准的目标是:

  1. 确定空闲注入比的有效范围

  2. 确定每个目标比率所需的补偿量

对每个目标比率的补偿由两部分组成:

  1. 稳态误差补偿

    这是为了抵消系统在没有额外唤醒(如外部中断)的情况下进入空闲时发生的错误。

  2. 动态误差补偿

    当空闲期间发生过多的唤醒时,可以通过减慢 CPU 活动来添加额外的空闲比率以抑制中断。

提供了一个 debugfs 文件供用户检查补偿进度和结果,例如在 Westmere 系统上:

[jacob@nex01 ~]$ cat
/sys/kernel/debug/intel_powerclamp/powerclamp_calib
controlling cpu: 0
pct confidence steady dynamic (compensation)
0       0       0       0
1       1       0       0
2       1       1       0
3       3       1       0
4       3       1       0
5       3       1       0
6       3       1       0
7       3       1       0
8       3       1       0
...
30      3       2       0
31      3       2       0
32      3       1       0
33      3       2       0
34      3       1       0
35      3       2       0
36      3       1       0
37      3       2       0
38      3       1       0
39      3       2       0
40      3       3       0
41      3       1       0
42      3       2       0
43      3       1       0
44      3       1       0
45      3       2       0
46      3       3       0
47      3       0       0
48      3       2       0
49      3       3       0

校准在运行时进行。没有离线方法可用。稳态补偿仅在所有相邻比率的置信水平达到满意水平时才使用。置信水平是根据运行时收集的干净数据累积的。在没有额外中断的期间收集的数据被认为是干净的。

为了补偿空闲期间过多的唤醒,当检测到此类情况时,会注入额外的空闲时间。目前,我们有一个简单的算法将注入比率加倍。一个可能的增强可能是限制触发中断,例如延迟电平触发中断的 EOI。但这对于调度器或 IRQ 核心代码来说,要做到非侵入性是一个挑战。

CPU 上线/下线

每 CPU 内核线程在收到 CPU 热插拔活动的通知后启动/停止。intel_powerclamp 驱动程序会跟踪钳制内核线程,即使它们在 CPU 下线事件后迁移到其他 CPU。

性能分析

本节描述了在多个系统上收集的一般性能数据,包括 Westmere (80P) 和 Ivy Bridge (4P, 8P)。

有效性和局限性

允许的空闲注入最大范围限制在 50%。如前所述,由于在强制空闲期间允许中断,过多的中断可能会导致效率降低。极端情况可能是执行 ping -f 来生成泛滥的网络中断而没有太多 CPU 确认。在这种情况下,空闲注入线程几乎无能为力。在大多数正常情况下,例如 scp 大文件时,应用程序可以通过 powerclamp 驱动程序进行节流,因为减慢 CPU 也会减慢网络协议处理,从而减少中断。

当控制 CPU 在运行时改变控制参数时,其余 CPU 可能需要额外的时间来跟上这些改变。在此期间,空闲注入会不同步,因此无法以预期的比率进入包 C 状态。但这种影响很小,因为在大多数情况下,目标比率的改变频率远低于空闲注入频率。

可扩展性

测试还显示,在 50% 空闲比率下,4P/8P Ivy Bridge 系统和 80P Westmere 服务器之间存在微小但可测量的差异。Westmere 需要更多的补偿才能达到相同的目标空闲比率。并且补偿随着空闲比率的增加而增加。上述原因构成了校准代码的必要性。

在 IVB 8P 系统上,与下线 CPU 相比,powerclamp 可以实现每瓦高达 40% 的更好性能。(通过对为所有运行 CPU 生成的每 CPU 计数线程求和的自旋计数器进行测量)。

使用和接口

powerclamp 驱动程序作为冷却设备注册到通用热层。目前,它未绑定到任何热区。

jacob@chromoly:/sys/class/thermal/cooling_device14$ grep . *
cur_state:0
max_state:50
type:intel_powerclamp

cur_state 允许用户设置所需的空闲百分比。向 cur_state 写入 0 将停止空闲注入。写入 1 到 max_state 之间的值将开始空闲注入。读取 cur_state 将返回实际和当前的空闲百分比。这可能与用户设置的值不同,因为当前空闲百分比取决于工作负载并包含自然空闲。当空闲注入被禁用时,读取 cur_state 返回值 -1 而不是 0,以避免将 100% 繁忙状态与禁用状态混淆。

使用示例

  • 注入 25% 的空闲时间

    $ sudo sh -c "echo 25 > /sys/class/thermal/cooling_device80/cur_state
    

如果系统不忙且已有超过 25% 的空闲时间,则 powerclamp 驱动程序将不会启动空闲注入。使用 Top 将不会显示空闲注入内核线程。

如果系统繁忙(下面的自旋测试)并且自然空闲时间少于 25%,powerclamp 内核线程将执行空闲注入。强制空闲时间被计为正常空闲,因为采用与空闲任务相同的通用代码路径。

在此示例中,显示了 24.1% 的空闲。这有助于系统管理员或用户在 powerclamp 驱动程序运行时确定性能下降的原因。

Tasks: 197 total,   1 running, 196 sleeping,   0 stopped,   0 zombie
Cpu(s): 71.2%us,  4.7%sy,  0.0%ni, 24.1%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   3943228k total,  1689632k used,  2253596k free,    74960k buffers
Swap:  4087804k total,        0k used,  4087804k free,   945336k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 3352 jacob     20   0  262m  644  428 S  286  0.0   0:17.16 spin
 3341 root     -51   0     0    0    0 D   25  0.0   0:01.62 kidle_inject/0
 3344 root     -51   0     0    0    0 D   25  0.0   0:01.60 kidle_inject/3
 3342 root     -51   0     0    0    0 D   25  0.0   0:01.61 kidle_inject/1
 3343 root     -51   0     0    0    0 D   25  0.0   0:01.60 kidle_inject/2
 2935 jacob     20   0  696m 125m  35m S    5  3.3   0:31.11 firefox
 1546 root      20   0  158m  20m 6640 S    3  0.5   0:26.97 Xorg
 2100 jacob     20   0 1223m  88m  30m S    3  2.3   0:23.68 compiz

测试表明,通过将 powerclamp 驱动程序用作冷却设备,基于 PID 的用户空间热控制器可以有效地管理 CPU 温度,前提是没有添加其他热影响。例如,Ultrabook 用户可以在特定温度(低于大多数活动跳变点)下编译内核。

模块参数

cpumask (读写)

一个用于注入空闲的 CPU 位掩码。位掩码的格式与 /proc/irq/*/smp_affinity 等其他子系统中使用的格式相同。掩码是逗号分隔的 32 位组。每个 CPU 对应一个位。例如,对于一个 256 CPU 系统,完整的掩码是:ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff

最右边的掩码用于 CPU 0-32。

max_idle (读写)

注入空闲时间占总 CPU 时间的最大比率,范围从 1% 到 100%。即使冷却设备的 max_state 始终为 100 (100%),此参数也允许添加最大空闲百分比限制。默认值为 50,以匹配 powerclamp 驱动程序的当前实现。如果 cpumask 包含系统中所有存在的 CPU,则不允许值超过 75。