Linux 内核 CPU 性能调节驱动程序历史文档全解:AMD PowerNow!、cpufreq-nforce2 与 pcc-cpufreq
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
导读
本文基于 Linux 内核源码树中的 Documentation/admin-guide/pm/cpufreq_drivers.rst(Legacy Documentation of CPU Performance Scaling Drivers),系统讲解三类具有代表性的历史 CPU 性能调节(CPU performance scaling)驱动程序:AMD 面向六至八代处理器的 PowerNow! 系列驱动(powernow-k6/k7/k8)、通过修改前端总线(FSB)来调频的 cpufreq-nforce2 驱动,以及基于固件与操作系统协调接口 PCC 的 pcc-cpufreq 驱动。文章将文档中的每一处细节与 drivers/cpufreq 目录下的真实源码一一印证,帮助读者理解这些驱动程序的设计动机、硬件交互方式、模块参数含义与 /sys 接口行为,为研究现代 cpufreq 驱动(如 acpi-cpufreq、amd-pstate、intel_pstate)提供宝贵的历史背景与对照参考。
文档定位:为何这些驱动被称为“历史文档”
文档开篇即声明,其内容为描述若干 CPU 性能调节驱动的历史性文档(historic documents),原文逐字保留(reproduced verbatim),连原始空白与缩进格式都原样呈现。这并不意味着这些驱动已从内核中删除——恰恰相反,它们的实现仍然完整地保留在当前仓库的 drivers/cpufreq 目录下:
powernow-k6.c、powernow-k7.c、powernow-k8.c对应 AMD PowerNow! 系列;cpufreq-nforce2.c对应 nForce2 FSB 调频驱动;pcc-cpufreq.c对应 PCC 接口驱动。
将历史文档与现行源码对照阅读,是理解“CPU 频率管理能力随硬件代际演进”的最佳途径:每一代处理器都在改变频率/电压控制的硬件实现,因此每一代都有对应的独立驱动。
AMD PowerNow! 驱动:按处理器代际划分的调频实现
命名与代际对应关系
PowerNow! 与 Cool'n'Quiet 是 AMD 对其处理器频率管理能力的两个品牌名:PowerNow! 主要面向移动处理器,Cool'n'Quiet 则是桌面处理器的对应特性。由于新世代处理器的硬件实现发生了变化,每个世代都有独立的 cpu-freq 驱动:
| 处理器代际 | 驱动 | 适用处理器 |
|---|---|---|
| 第 6 代 | powernow-k6 | Mobile K6-2+ / K6-3+ |
| 第 7 代 | powernow-k7 | Athlon、Duron、Geode |
| 第 8 代 | powernow-k8 | Athlon、Athlon 64、Opteron、Sempron |
文档特别提醒两个实用要点:
- 驱动不会在“错误”的硬件上加载,因此当你无法确定哪个驱动正确时,可以逐个尝试,安全无副作用。这一点在 Kconfig.x86 中也有印证:
X86_POWERNOW_K6依赖X86_32,X86_POWERNOW_K8依赖ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ,构建时按配置裁剪,运行时则靠 CPU 特性探测决定是否加载。 - 并非所有处理器都具备变频(及调压)能力。驱动通过
cpuid指令检测该能力,能力缺失时驱动会拒绝加载。
频率与电压数据的来源:BIOS 表与 ACPI
驱动依赖BIOS 提供的表来获取适用于特定平台的频率和电压信息;若 BIOS 不提供这些表,频率切换功能将不可用。
- 对 powernow-k7 与 powernow-k8,BIOS 提供的数据可能来自PSB 表(PowerNow! BIOS 表)或ACPI 对象(_PSS 等性能状态)。
- ACPI 支持仅在开启
CONFIG_ACPI_PROCESSOR时可用(见 Kconfig.x86 中X86_POWERNOW_K7_ACPI的依赖关系)。 - powernow-k8在配置了 ACPI 时优先尝试 ACPI,失败则回退到 PSB 表。
- powernow-k7则优先使用 PSB 支持,失败后才回退到 ACPI;并提供一个名为
acpi_force的模块参数,强制改用 ACPI 而不用 PSB。
从源码看,powernow-k8 对 PSB/PST 表的处理逻辑位于 powernow-k8.c:驱动扫描 BIOS 内存查找 PSB 头(PSB_ID_STRING),校验表版本必须为 v1.4,逐项解析 PST 记录;若既找不到 PSB 表也找不到 ACPI _PSS 对象,则打印FW_BUG "No PSB or ACPI _PSS objects"并失败。transition latency(切换延迟)也分别按 PSB 或 ACPI 两条路径取值,见get_transition_latency()(powernow-k8.c)。powernow-k7 的源码 powernow-k7.c 定义了psb_s(表头:签名、表版本、flags、settlingtime、PST 数量)与pst_s(每条性能状态记录:cpuid、FSB 速度、maxfid、startvid、状态数)两个结构体,与文档描述的 PSB/PST 机制完全对应。
powernow-k6 的模块参数与频率表
从源码 powernow-k6.c 可以看到该驱动暴露的两个模块参数:
max_multiplier:最大倍频,允许取值 20、30、35、40、45、50、55、60(即 2.0x–6.0x,乘以 10 表示);bus_frequency:总线频率,单位 kHz。
驱动的倍频与 CPU 内部寄存器码(register code)的对应关系通过两张表维护:index_to_register[8]与register_to_index[8](powernow-k6.c),实现“频率表索引 ↔ 硬件寄存器值”的双向映射。usual_frequency_table[](powernow-k6.c)则列出常见“FSB × 倍频”组合,例如 100 MHz × 5.5 = 550 MHz、120 MHz × 6 = 720 MHz,可直接作为调频目标参考。
cpufreq-nforce2:通过修改 FSB 调节频率
工作原理
cpufreq-nforce2通过改变 nVidia nForce2 平台的FSB(前端总线)频率来实现 CPU 调频。文档指出其在这类平台上效果优于其他平台,原因是CPU 的 FSB 可以独立于 PCI/AGP 时钟被控制,调频不会连带干扰外设总线时钟。
源码 cpufreq-nforce2.c 的实现细节印证了这一点:
- 驱动通过 PCI 配置空间直接操作芯片组的 PLL 寄存器(
NFORCE2_PLLREG、NFORCE2_PLLADR、NFORCE2_PLLENABLE,见 cpufreq-nforce2.c); - 晶振频率固定为 25 MHz(
NFORCE2_XTAL),FSB = 25 × multiplier / divider; - 芯片组的 PLL 值由
nforce2_calc_pll()通过遍历 multiplier/divider 组合反算得出(cpufreq-nforce2.c),并以写满 64 个寄存器槽位的方式写入(nforce2_write_pll(),cpufreq-nforce2.c); - FSB 切换采用逐 MHz 步进的渐进方式(
nforce2_set_fsb(),cpufreq-nforce2.c),每次只变化 1 MHz 并重算 PLL 值,以降低稳定性风险。
值得注意:该驱动源码头部注明“基于逆向工程信息”(reverse engineered information),并带有“BIG FAT DISCLAIMER: Work in progress code. Possiblydangerous”警告,说明这类直接操作芯片组寄存器的驱动存在天然风险,这也解释了文档为何要强调 FSB 可用范围向下受限的问题。
模块参数:fid 与 min_fsb
文档给出的两个模块选项,在源码中有完全对应的定义(cpufreq-nforce2.c):
| 参数 | 含义 | 默认值 |
|---|---|---|
fid | 倍频 × 10(例如 8.5 = 85) | 未设置时根据当前 CPU 速度与 FSB 反算 |
min_fsb | 最小 FSB | 默认 = 开机时 FSB − 50 MHz |
源码中对应的常量定义:最小 FSB 硬下限为 50 MHz(NFORCE2_MIN_FSB),默认下限与开机 FSB 的安全距离为 50 MHz(NFORCE2_SAFE_DISTANCE),见 cpufreq-nforce2.c。
重要限制:可用范围向下受限
文档以醒目字体(IMPORTANT)强调:可用 FSB 范围是向下受限的,且不同系统的最小可用 FSB 可能不同;例如对于以 200 MHz 启动的系统,150 MHz 通常总是可用。从源码看,nforce2_set_fsb()会先校验目标 FSB 不超过max_fsb(即开机 FSB)且不低于NFORCE2_MIN_FSB,并在每次切换时保持tfsb落在[min_fsb, max_fsb]区间内(cpufreq-nforce2.c),一旦超出即返回-EINVAL。这解释了“向下受限”的根源:调频动作只能发生在系统当前 FSB 及其安全下限之间。
pcc-cpufreq:基于固件协调接口的处理器时钟控制
PCC 接口与设计动机
Processor Clocking Control(PCC)是平台固件(platform firmware)与 OSPM(操作系统电源管理)之间的接口,用于在固件与操作系统之间协调处理器性能(即频率)。pcc-cpufreq驱动(源码见 pcc-cpufreq.c)让 OSPM 得以利用该接口。
工作机制可以概括为:OS 通过 PCC 接口告知平台固件它希望某个逻辑处理器运行在什么频率上,固件负责尝试达成该频率。如果固件无法满足目标频率请求,通常意味着存在功耗预算约束,即发生了“power capping”(功率封顶)——此时固件正在主动限制功耗。
PCC 的技术基础是:
- 共享内存区域(shared memory region):OS 与平台固件之间的通信通道;
- 门铃(doorbell):OS 用来通知固件“已发送命令”的机制;
- ACPI PCCH() 方法:用于发现 PCC 共享内存区域的位置;共享内存区域的头部包含“command”和“status”接口,PCCH() 还提供访问平台门铃的细节;
- ACPI PCCP() 方法:为每个逻辑处理器实现,用于发现输入/输出缓冲区在共享内存区域中的偏移量。
源码中的结构体定义与之一一对应:pcc-cpufreq.c 的struct pcc_header包含 signature、command、status、latency、minimum_time、maximum_time、nominal(标称频率)、throttled_frequency、minimum_frequency 等字段;pcc-cpufreq.c 的struct pcc_cpu保存每个 CPU 的input_offset与output_offset。探测流程pcc_cpufreq_evaluate()(pcc-cpufreq.c)先检查\_SB下是否存在PCCH方法,再解析 PCCH() 返回的内存资源描述与门铃寄存器描述,映射共享内存并读取头部字段;pcc_get_offset()(pcc-cpufreq.c)则为每个 CPU 求值 PCCP(),取得输入/输出缓冲区偏移。
PCC 支持的命令
PCC 接口支持两个命令:
- Get Average Frequency(获取平均频率):OSPM 用它查询处理器自上次命令完成以来的运行频率。输出缓冲区给出逻辑处理器的平均未暂停(unhalted)频率,表示为标称(即最大)CPU 频率的百分比;同时输出缓冲区还标明 CPU 频率是否受功耗预算条件限制。对应源码
pcc_get_freq()(pcc-cpufreq.c):写入输入缓冲区、写入CMD_GET_FREQ命令字、敲击门铃并轮询状态直到CMD_COMPLETE,然后以nominal × (output & 0xff) / 100计算当前频率;输出字节的高 8 位((output_buffer >> 8) & 0xff)若非0xff,则表示频率正被临时封顶(capped)。 - Set Desired Frequency(设置期望频率):OSPM 用它向固件传达逻辑处理器的期望频率;输出缓冲区当前被 OSPM 忽略,下一次 “Get Average Frequency” 会告知 OSPM 期望频率是否达成。对应源码
pcc_cpufreq_target()(pcc-cpufreq.c):按1 | ((target_freq * 100) / (nominal * 1000)) << 8组装输入缓冲区(bit0 表示“新命令”,高字节为频率占标称频率的百分比),写入CMD_SET_FREQ命令字,敲击门铃并等待完成。
与原生 P-state 驱动的关系
文档明确:PCC 模式启用时,平台不会向 OSPM 暴露处理器性能或节流状态(_PSS、_TSS 及相关 ACPI 对象),因此原生 P-state 驱动(如面向 Intel 的 acpi-cpufreq、面向 AMD 的 powernow-k8)不会加载。但OSPM 仍然掌握策略控制权:调速器(governor,例如 ondemand)根据服务器工作负载计算每个处理器所需的性能,PCC 驱动填充命令接口与输入缓冲区,将请求交给平台固件,由固件负责交付所请求的性能。
另一个关键特性是PCC 命令的“全局(global)”作用域:每个 PCC 命令都会影响系统中的所有逻辑 CPU,因此 PCC 具备“组更新(group updates)”能力——OS 只需一次调用 BIOS 即可获取/设置系统中所有逻辑 CPU 的频率。源码中pcc_cpufreq_probe()在 CPU 数大于 4 时会为驱动设置CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING标志并打印错误信息(pcc-cpufreq.c),建议在 CPU 过多的系统上通过 BIOS 设置启用其他 scaling 驱动,这正是该全局作用域特性带来的实际约束。
受影响的平台与加载行为
PCC 驱动会在满足以下条件的任何系统上加载:
- 平台固件支持 PCC 接口及配套的 PCCH()、PCCP() 方法;
- 固件承担管理硬件时钟控制、以交付所请求处理器性能的职责。
文档指出,目前某些 HP ProLiant 平台实现了 PCC 接口,在这些平台上 PCC 是“默认”选择。但该接口可以通过 BIOS 设置禁用;此时(以及在未实现 PCC 接口的平台上),PCC 驱动会静默加载失败(fail to load silently)。
驱动加载时打印最低与最高 CPU 频率限制,示例消息:
pcc-cpufreq: (v1.00.00) driver loaded with frequency limits: 1600 MHz, 2933 MHz这意味着 OSPM 可以请求 CPU 运行在 1600 MHz 与 2933 MHz 之间的任意频率。当前源码中的版本字符串为"1.10.00"(pcc-cpufreq.c),打印语句位于 pcc-cpufreq.c。文档还解释了版本号格式v.xy.ab的语义:ab随驱动的 bug 修复/功能增强而递增,xy则是驱动所遵循的 PCC 规范版本。
另外,内部层面驱动无需将“目标频率”转换为对应的 P-state——频率请求是连续的,不与离散 P-state 绑定。从驱动结构看,struct cpufreq_driver pcc_cpufreq_driver(pcc-cpufreq.c)只实现了.verify、.target、.get、.init四个回调,没有频率表(freq table)相关回调,与文档描述完全吻合。
驱动与 /sys 接口的细节
文档针对四个 /sys 字段逐一说明了 PCC 驱动下的行为:
scaling_available_frequencies:不创建
PCC 驱动不会在 /sys 中创建scaling_available_frequencies。原因在于 BIOS 会尝试达成调速器请求的范围内任意频率,频率不必严格关联某个 P-state,因此无需列出中间频率。
cpuinfo_transition_latency:恒为 0
cpuinfo_transition_latency字段为0。因为 PCC 规范目前没有包含用于暴露该值的字段。
cpuinfo_cur_freq:两个典型现象
文档给出两种常见情况:
A) 因 “turbo boost” 出现的偏差:cpuinfo_cur_freq常会显示与scaling_available_frequencies、scaling_cur_freq或scaling_max_freq不同的值。若满足特定条件,BIOS 能达到比 OSPM 请求值略高的速度,例如:
scaling_cur_freq : 2933000 cpuinfo_cur_freq : 3196000B) 舍入误差:由于驱动从 BIOS 获取的当前频率是标称频率的“百分比”,scaling_cur_freq与cpuinfo_cur_freq显示的值有时不匹配,例如:
scaling_cur_freq : 1600000 cpuinfo_cur_freq : 1583000本例中标称频率为 2933 MHz,驱动获取的当前频率为标称频率的 54%:54% × 2933 MHz ≈ 1583 MHz。标称频率即处理器最大频率,通常对应 P0 P-state。这一计算逻辑在源码 pcc-cpufreq.c 中清晰可见:curr_freq = (nominal * (output & 0xff) / 100) * 1000。
related_cpus:与 affected_cpus 相同
related_cpus字段与affected_cpus完全一致,例如:
affected_cpus : 4 related_cpus : 4原因在于 PCC 驱动目前不求值 _PSD,且支持 PCC 的平台不实现 SW_ALL 依赖协调语义,因此 OSPM 无需执行任何协调动作来确保所有依赖 CPU 请求相同频率。
使用限制(Caveats)
文档明确指出一个重要的兼容性限制:cpufreq_stats模块在其现有形式下无法与 PCC 驱动协同工作。因为cpufreq_stats提供的是针对每个 P-state 的统计信息,而 PCC 驱动并不基于 P-state 工作。这意味着启用 PCC 驱动的系统上,基于 P-state 的统计功能将不适用。
配置与加载方式小结
结合 Kconfig.x86 与 drivers/cpufreq/Makefile,这些驱动均以模块(tristate)形式供选择:
- X86_POWERNOW_K6:模块名为
powernow-k6,仅限X86_32; - X86_POWERNOW_K7:模块名为
powernow-k7,仅限X86_32;搭配X86_POWERNOW_K7_ACPI(依赖ACPI_PROCESSOR)启用 ACPI 支持; - X86_POWERNOW_K8:模块名为
powernow-k8,依赖ACPI && ACPI_PROCESSOR && X86_ACPI_CPUFREQ(K10 及更新处理器已由 acpi-cpufreq 接管); - X86_CPUFREQ_NFORCE2:目标文件为
cpufreq-nforce2.o(见 Makefile),模块加载参数为fid与min_fsb; - X86_PCC_CPUFREQ:模块名为
pcc-cpufreq,依赖ACPI && ACPI_PROCESSOR(Kconfig.x86),其帮助文本明确指向本文档Documentation/admin-guide/pm/cpufreq_drivers.rst。
由于这些驱动在“错误”硬件上不会加载,实际使用中可将对应模块加入启动加载列表,由驱动自身的硬件探测机制决定是否生效;PCC 驱动则以late_initcall注册为平台驱动(pcc-cpufreq.c),若已存在其他 cpufreq 驱动则主动跳过初始化。
总结与历史意义
这三个驱动分别代表了 CPU 频率管理的三条经典技术路线:处理器内置的 FID/VID 状态表(AMD PowerNow!,依赖 BIOS PSB 表或 ACPI _PSS)、通过芯片组修改外频(nForce2 FSB/PLL)、OS 与固件通过共享内存协商频率(PCC)。它们共同展示了内核 cpufreq 框架“驱动只负责与硬件交互、调速器负责决策策略”的核心分层思想,也为理解现代驱动(acpi-cpufreq 的 P-state 表、amd-pstate 的精细频率范围、intel_pstate 的内置调速器)提供了清晰的历史坐标系。对于希望深入 cpufreq 子系统源码的开发者,先读懂这三份“历史文档”及其对应实现,是性价比极高的入门路径。
【免费下载链接】linuxLinux kernel source tree项目地址: https://gitcode.com/GitHub_Trending/li/linux
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考