参考4.9内核,还是先看文档描述:

Introduction

Some CPUs support a functionality to raise the operating frequency of
some cores in a multi-core package if certain conditions apply, mostly
if the whole chip is not fully utilized and below it’s intended thermal
budget. The decision about boost disable/enable is made either at hardware
(e.g. x86) or software (e.g ARM).
On Intel CPUs this is called “Turbo Boost”, AMD calls it “Turbo-Core”,
in technical documentation “Core performance boost”. In Linux we use
the term “boost” for convenience.

有些多核CPU系统中支持提高操作频率, 由硬件or软件来决定,Linux用boost来表示这个功能。一般ARM(Exynos)使用的是SW,x86使用的是HW。

有个boost开关用户可以控制禁用使能:

To allow the user to toggle the boosting functionality, the cpufreq core
driver exports a sysfs knob to enable or disable it. There is a file:
/sys/devices/system/cpu/cpufreq/boost

ok,来看相关代码,路径在drivers/cpufreq/:

static bool cpufreq_boost_supported(void)
{
return likely(cpufreq_driver) && cpufreq_driver->set_boost;
}

如果支持提高频率,drv就要配置->set_boost,可以看到acpi-cpufreq.c配了,还有一处是cpufreq.c里也配了,分别看下:

static void __init acpi_cpufreq_boost_init(void)
{
if (boot_cpu_has(X86_FEATURE_CPB) || boot_cpu_has(X86_FEATURE_IDA)) {
msrs = msrs_alloc();

if (!msrs)
return;

acpi_cpufreq_driver.set_boost = set_boost;
acpi_cpufreq_driver.boost_enabled = boost_state(0);

可见是x86 hw本身就支持,只要满足feature: X86_FEATURE_CPB or X86_FEATURE_IDA,默认禁用(boost_enabled=0)。

static int __init acpi_cpufreq_init(void)
{
...
acpi_cpufreq_boost_init();

ret = cpufreq_register_driver(&acpi_cpufreq_driver);
if (ret) {
...
}

late_initcall(acpi_cpufreq_init);

acpi drv在注册到core前就定下了。还有一处在core里:

int cpufreq_enable_boost_support(void)
{
if (!cpufreq_driver)
return -EINVAL;

if (cpufreq_boost_supported())
return 0;

cpufreq_driver->set_boost = cpufreq_boost_set_sw; //tj: here

/* This will get removed on driver unregister */
return create_boost_sysfs_file();
}
EXPORT_SYMBOL_GPL(cpufreq_enable_boost_support);

cpufreq-dt.c会call:

static int cpufreq_init(struct cpufreq_policy *policy)
{
...
/* Support turbo/boost mode */
if (policy_has_boost_freq(policy)) {
/* This gets disabled by core on driver unregister */
ret = cpufreq_enable_boost_support();
if (ret)
static struct cpufreq_driver dt_cpufreq_driver = {
.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = set_target,
.get = cpufreq_generic_get,
.init = cpufreq_init,

是挂到了->init中去设置->set_boost,从名字看应该是sw boost。判断是否支持:

bool policy_has_boost_freq(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *pos, *table = policy->freq_table;

if (!table)
return false;

cpufreq_for_each_valid_entry(pos, table)
if (pos->flags & CPUFREQ_BOOST_FREQ)
return true;

return false;
}

和频率表的生成(CPUFREQ_BOOST_FREQ)有关。跟下这个flag:cpufreq目录下没有,opengrok搜了下有:

/kernel/msm-4.9/drivers/base/power/opp/
H A D cpu.c 85 freq_table[i].flags = CPUFREQ_BOOST_FREQ;

opp是啥?和pm有关:

/**
* dev_pm_opp_init_cpufreq_table() - create a cpufreq table for a device
...
int dev_pm_opp_init_cpufreq_table(struct device *dev,
struct cpufreq_frequency_table **table)
{
...
/* Is Boost/turbo opp ? */
if (dev_pm_opp_is_turbo(opp))
freq_table[i].flags = CPUFREQ_BOOST_FREQ;
}

dev_pm_opp_is_turbo():

bool dev_pm_opp_is_turbo(struct dev_pm_opp *opp)
{
struct dev_pm_opp *tmp_opp;

opp_rcu_lockdep_assert();

tmp_opp = rcu_dereference(opp);
if (IS_ERR_OR_NULL(tmp_opp) || !tmp_opp->available) {
pr_err("%s: Invalid parameters\n", __func__);
return false;
}

return tmp_opp->turbo;
}

取决于->turbo,是从设备树而来:

new_opp->turbo = of_property_read_bool(np, "turbo-mode");

搜了下三星的Exynos会用到:

arch/arm/boot/dts/exynos4412.dtsi:137:			turbo-mode;

翻了下git log原来就是三星加的software boost for exynos arch,只不过后来放到了cpufreq-dt.c中,流程上放到了register之后。

dt_cpufreq_probe
|-> cpufreq_register_driver
|-> cpufreq_driver->init(cpufreq_init)

register里还有个创建sysfs boost的,可见是给前文的hardware(x86) boost用。

int cpufreq_register_driver(struct cpufreq_driver *driver_data)
{
...
if (cpufreq_boost_supported()) {
ret = create_boost_sysfs_file();
if (ret)
goto err_null_driver;
}

另外如果boost使能了,boost freq也是min/max的备选:

int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table)
{
...
cpufreq_for_each_valid_entry(pos, table) {
freq = pos->frequency;

if (!cpufreq_boost_enabled() //tj: here
&& (pos->flags & CPUFREQ_BOOST_FREQ))
continue;

pr_debug("table entry %u: %u kHz\n", (int)(pos - table), freq);
if (freq < min_freq)
min_freq = freq;
if (freq > max_freq)
max_freq = freq;
}

policy->min = policy->cpuinfo.min_freq = min_freq;
policy->max = policy->cpuinfo.max_freq = max_freq;

sysfs的scaling_available是不包含boost freq的:

/**
* show_scaling_available_frequencies - show available normal frequencies for
* the specified CPU
*/
static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy,
char *buf)
{
return show_available_freqs(policy, buf, false);
}
cpufreq_attr_available_freq(scaling_available);
EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs);

scaling_boost是show boost freq:

/**
* show_available_boost_freqs - show available boost frequencies for
* the specified CPU
*/
static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy,
char *buf)
{
return show_available_freqs(policy, buf, true);
}
cpufreq_attr_available_freq(scaling_boost);

来看下show_available_freqs:

static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf,
bool show_boost)
{
ssize_t count = 0;
struct cpufreq_frequency_table *pos, *table = policy->freq_table;

if (!table)
return -ENODEV;

cpufreq_for_each_valid_entry(pos, table) {
/*
* show_boost = true and driver_data = BOOST freq
* display BOOST freqs
*
* show_boost = false and driver_data = BOOST freq
* show_boost = true and driver_data != BOOST freq
* continue - do not display anything
*
* show_boost = false and driver_data != BOOST freq
* display NON BOOST freqs
*/
if (show_boost ^ (pos->flags & CPUFREQ_BOOST_FREQ))
continue;

count += sprintf(&buf[count], "%d ", pos->frequency);
}

这里driver_data指的就是CPUFREQ_BOOST_FREQ这个flag了,所以逻辑就是:

  • 当显示boost freq时看cpufreq table有没有boost freq,如果有就显示;
  • 当显示normal freq时就是从cpufreq table中过滤boost freq。

Done.