Linux源码 cppc动态调频
阅读数:867 评论数:0
跳转到新版页面分类
Linux
正文
一、概述
CPPC(Collaborative Processor Performance Control)是一种由 ACPI(高级配置和电源接口)定义的硬件和操作系统之间的接口,用于动态调节处理器的性能。这种机制在现代处理器上尤其重要,因为它允许操作系统根据负载需求和热设计功耗(TDP)限制来调整处理器性能状态(P-state)。
在 Linux 系统中,CPU 动态调频通常是由 CPU 频率调节框架(cpufreq)管理的,该框架提供了多种调频策略(称为“governors”),如“performance”(始终运行在最高频率)、“powersave”(优先节能)、“userspace”(用户自定义频率)以及“ondemand”和“conservative”(根据当前负载动态调整频率)。
对于支持 CPPC 的系统,Linux 内核可以通过 ACPI 的 CPPC 接口与硬件协同工作,以实现更精细的性能控制。这通常涉及到新一代的处理器,如 AMD 的 Zen 架构处理器和部分新型号的 Intel 处理器。
要启用和管理 CPPC 动态调频,你需要确保几件事情:
-
内核支持:确保你的 Linux 内核支持 CPPC。较新的内核版本(如 5.x 系列)可能已经内置了这一功能。
-
ACPI CPPC 驱动程序:内核必须包含适当的 ACPI CPPC 驱动程序。这通常是默认启用的,但是在定制内核时需要注意。
-
BIOS/UEFI 设置:在某些情况下,你可能需要在 BIOS/UEFI 设置中启用 CPPC 支持。
-
操作系统工具:使用像
cpupower
这样的工具来管理和查看 CPU 频率策略。
例如,要查看当前的 CPU 频率策略,可以使用以下命令:
cpupower frequency-info
要设置特定的调频策略,可以使用:
cpupower frequency-set -g [governor_name]
其中 [governor_name]
是你想要选择的调频策略。
请注意,CPPC 动态调频的细节和配置可能会因不同的硬件和 Linux 发行版而异。对于特定硬件的详细信息,查阅硬件供应商的文档通常是最好的做法。如果你在尝试配置 CPPC 动态调频时遇到问题,你可能需要查看内核文档、硬件手册或者你的 Linux 发行版的相关文档。
二、相关代码
CPC的全称是Per cpu table called,是bios提供的一组acpi表,用于设置cpu的频率。这组acpi表如下:
/*
* An example CPC table looks like the following.
*
* Name(_CPC, Package()
* {
* 17,
* NumEntries
* 1,
* // Revision
* ResourceTemplate(){Register(PCC, 32, 0, 0x120, 2)},
* // Highest Performance
* ResourceTemplate(){Register(PCC, 32, 0, 0x124, 2)},
* // Nominal Performance
* ResourceTemplate(){Register(PCC, 32, 0, 0x128, 2)},
* // Lowest Nonlinear Performance
* ResourceTemplate(){Register(PCC, 32, 0, 0x12C, 2)},
* // Lowest Performance
* ResourceTemplate(){Register(PCC, 32, 0, 0x130, 2)},
* // Guaranteed Performance Register
* ResourceTemplate(){Register(PCC, 32, 0, 0x110, 2)},
* // Desired Performance Register
* ResourceTemplate(){Register(SystemMemory, 0, 0, 0, 0)},
* ..
* ..
* ..
*
* }
* Each Register() encodes how to access that specific register.
* e.g. a sample PCC entry has the following encoding:
*
* Register (
* PCC,
* AddressSpaceKeyword
* 8,
* //RegisterBitWidth
* 8,
* //RegisterBitOffset
* 0x30,
* //RegisterAddress
* 9
* //AccessSize (subspace ID)
* 0
* )
* }
*/
那cpc表具体要怎么工作呢?具体在driver/cpufreq/cppc_cpufreq.c中。
这里的cppc_cpufreq_init是入口函数,这个函数向cpufreq的framework注册了一个可以调频的cpu driver
static int __init cppc_cpufreq_init(void)
{
ret = cpufreq_register_driver(&cppc_cpufreq_driver);
if (ret)
goto out;
}
static struct cpufreq_driver cppc_cpufreq_driver = {
.flags = CPUFREQ_CONST_LOOPS,
.verify = cppc_verify_policy,
.target = cppc_cpufreq_set_target,
.get = cppc_cpufreq_get_rate,
.init = cppc_cpufreq_cpu_init,
.stop_cpu = cppc_cpufreq_stop_cpu,
.name = "cppc_cpufreq",
};
//cppc_cpufreq_driver 最终的函数就是target,最终cpu调频就是通过target 这个回调函数来实现
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct cppc_cpudata *cpu;
struct cpufreq_freqs freqs;
u32 desired_perf;
int ret = 0;
cpu = all_cpu_data[policy->cpu];
//得到要设置的频率
desired_perf = cppc_cpufreq_khz_to_perf(cpu, target_freq);
/* Return if it is exactly the same perf */
if (desired_perf == cpu->perf_ctrls.desired_perf)
return ret;
cpu->perf_ctrls.desired_perf = desired_perf;
freqs.old = policy->cur;
freqs.new = target_freq;
cpufreq_freq_transition_begin(policy, &freqs);
//通过acpi 提供的的接口来设置cpu 频率
ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls);
cpufreq_freq_transition_end(policy, &freqs, ret != 0);
if (ret)
pr_debug("Failed to set target on CPU:%d. ret:%d\n",
cpu->cpu, ret);
return ret;
}
这里的cppc_set_perf实现在driver/acpi/cppc_acpi.c中实现,通过这个接口可以通过固件来设置cpu频率。