linux cpufreq framework(5)_ARM big Little driver

本文深入探讨了 Linux 内核如何支持 ARM 的 big.LITTLE 架构,通过对核心组件 armbiglittle cpufreq driver 和 armbLswitcher driver 的分析,揭示了系统在不同性能核心间智能切换的工作原理。
1. 前言

也许大家会觉得奇怪:为什么Linux kernel把对ARM big·Lttile的支持放到了cpufreq的框架中?

众所周知,ARM的big·Little架构,也称作HMP(具体可参考“Linux CPU core的电源管理(2)_cpu topology”中相关的介绍),通过在一个chip中封装两种不同类型的ARM core的方式,达到性能和功耗的平衡。这两类ARM Core,以cluster为单位,一类为高性能Core(即big core),一类为低性能Core(即Little core),通过它们的组合,可以满足不同应用场景下的性能和功耗要求,例如:非交互式的后台任务、或者流式多媒体的解码,可以使用低功耗的Little core处理;突发性的屏幕刷新,可以使用高性能的big core处理。

那么问题来了,Linux kernel怎么支持这种框架呢?

注1:本文很多理论性的表述,或多或少的理解并翻译自:“http://lwn.net/Articles/481055/”,感兴趣的读者可以自行阅读。

注2:本文基于linux-3.18-rc4内核,其它版本内核可能会稍有不同。

2. Linux kernel支持ARM big·Lttile框架的思路

以一个包含两个cluster,cluster0有4个A15 core,cluster1有4个A7 core为例,我们会很自然的想到,可以把这8个core统统开放给kernel,让kernel的调度器(scheduler)根据系统的实际情况,决定哪些任务应该在哪些core上执行。但这存在一个问题:

当前linux kernel的scheduler,都是针对SMP系统设计的,而SMP系统中所有CPU core的性能和功耗都是相同的。

换句话说:kernel中还没有适用于big·Little架构的scheduler。怎么办?等待这样的一个scheduler出现?芯片厂商当然等不及,市场上已经出现了很多这种架构的芯片。为了应对这种软件滞后于硬件的现象(当前这种现象比比皆是,可能是硬件的成本比软件的人力成本低吧),ARM公司提出了这样一种软件解决方案(这只是ARM提出的解决方案的一种,后续我们介绍PSCI时,还会接触其它方案,这里就不再提及):

使用一个hypervisor(可以参考“ARMv8-a架构简介”中有关hypervisor的介绍),利用虚拟化扩展,将8个A15+A7 core虚拟为4个A15 core,这样OS kernel就可以以SMP的方式,和这4个虚拟的core交互。当OS需要的时候,可以通过一个hypervisor指令,让某一个虚拟的core在A15和A7两种模式下切换,所有的切换动作,包括IRQ、timer等的迁移,都由hypervisor负责,对OS是透明的。

上面的方法有两个缺点:

1)这8个core不能任意的组合使用

2)必须存在hypervisor,会增加系统的复杂度

linux 借鉴了这个思路,将ARM hypervisor相关的实现逻辑,移植到kernel中,借助cpufreq framework,来实现上面的功能。之所以将hypervisor移植到kernel,是为了降低对ARM hypervisor code的耦合要求(解决上面的缺点2,当然,缺点1暂时无法解决)。而之所以使用cpufreq的框架,其中的思考如下:

1)抛开在big core和Little core之间切换的代价不谈,对scheduler来说,big core和Little core的区别仅仅在core的性能和功耗上,这恰恰和cpufreq framework的关注点一致:频率降低,性能降低,功耗降低;频率增大,性能增强,功耗增大。因此,可以将big、Little的切换,嵌入到CPU的频率调整中,低范围的频率段,对应Little core,高范围的频率段,对应big core。

2)对kernel scheduler而言,上面的8个core只有4个可见,这4个可见的core具有较宽的频率范围,并在big·Little switcher使能的情况下,根据频率需求的情况,自动在big和Little之间切换。

3)因此,使用cpufreq框架,巧妙的将不对称HMP架构,转化为了SMP架构,这样,kernel现有的scheduler就派上用场了。

3. ARM big·Little driver的软件框架

基于上面的思考,linux kernel使用如下的软件框架实现ARM big·Little切换功能:

linux_arm_big_little

arm big little cpufreq driver,位于drivers/cpufreq/目录中,负责和cpufreq framework对接,以CPU调频的形式,实现ARM big·Little的切换。

arm bL switcher,是一个arch-dependent的driver,提供实际的切换操作。

3.1 arm big little cpufreq driver

arm big little cpufreq driver位于drivers/cpufreq目录下,由三个文件组成:

arm_big_little.c 
arm_big_little.h 
arm_big_little_dt.c

主要提供如下的功能(以本文参考的“linux-3.18-rc4” kernel为准):

1)支持A15和A7两个cluster。

2)当bL switching处于disable状态(bL switching的状态由arm bL switcher driver控制,3.2节会介绍)时,该driver就是一个普通的cpufreq driver,并为每个cluster提供一个frequency table(保存在freq_table[0]、freq_table[1]中)。

此时所有的big、Little core对系统都可见,每个core都可以基于cpufreq framework调整频率。

3)当bL switching处于enable状态时,该driver变成一个特殊的cpufreq driver,在调整频率的时候,可以根据情况,切换core的cluster。以第2章所描述的8个core为例:

此时只有4个虚拟的core对系统可见(由arm bL switcher driver控制,3.2节会介绍),系统不关心这些core属于哪个cluster、是big core还是Little core;

确切的说,每一个虚拟的core,代表了属于两个cluster的CPU对,可以想象为big+Little组合,只是同一时刻,只有一个core处于enable状态(big or Little);

该driver会搜集2个cluster的frequency table,并合并成一个(保存在freq_table[2]中)。合并后,找出这些frequency中big core最小的那个(clk_big_min)以及Little core最大的那个(clk_little_max);

基于cpufreq framework进行频率调整时,如果所要求的频率小于clk_big_min,则将该虚拟core所对应的Little core使能,如果所要求得频率大于clk_little_max,则将该虚拟core所对应的big core使能。

4)基于上面的描述,ARM big·Little driver会把big·Little架构下的8个CPU core,以“big+Little”的形式组合成4对,同一时刻,每对组合的core只有一个处于运行状态,这就是Linux kernel ARM big·Little架构的核心思路。

3.2 arm bL switcher driver

arm bL switcher driver是一个arch-dependent的driver,以ARM平台为例,其source code包括:

arch/arm/common/bL_switcher.c 
arch/arm/common/bL_switcher_dummy_if.c

该driver的功能如下:

1)提供bL switcher功能的enable和disable控制

2)通过sysfs,允许用户空间软件控制bL switcher的使能与否

接口文件位于:

/sys/kernel/bL_switcher/active

读取可以获取当前的使能情况,写1 enable,写0 disable。

3)为每个虚拟的CPU core(big+Little组合),创建一个线程,实现最终的cluster切换。该部分是平台相关的,后面将会以ARM平台为例介绍具体的过程。

4. 代码分析

本章以ARM平台为例,结合kernel source code,从初始化以及cluster切换两个角度,介绍ARM big·Little driver的核心功能。

4.1 初始化

和ARM big·Little driver有关的初始化过程主要分为三个部分:

1)CPU core的枚举和初始化,具体可参考“Linux CPU core的电源管理(5)_cpu control及cpu hotplug”中有关possible CPU、present CPU的描述。

2)arm big little cpufreq driver的初始化,为每个cluster创建一个frequency table,并主持相应的cpufreq driver。

3)arm bL switcher driver,初始化bL switcher,并使能bL switcher。

下面我们重点介绍步骤2和步骤3。

4.1.1 arm big little cpufreq driver的初始化

还是以包含两个cluster,每个cluster有4个CPU core(A15和A7)的系统为例,由“Linux CPU core的电源管理(5)_cpu control及cpu hotplug”得描述可知,start_kernel之后,系统的possible CPU包含所有的8个core。

然后arm big little cpufreq driver出场了,其init接口位于“drivers/cpufreq/arm_big_little_dt.c”中,如下:

  1: static int generic_bL_probe(struct platform_device *pdev)
  2: {
  3:         struct device_node *np;
  4: 
  5:         np = get_cpu_node_with_valid_op(0);
  6:         if (!np)
  7:                 return -ENODEV;
  8: 
  9:         of_node_put(np);
 10:         return bL_cpufreq_register(&dt_bL_ops);
 11: }
 12: 
 13: static int generic_bL_remove(struct platform_device *pdev)
 14: {
 15:         bL_cpufreq_unregister(&dt_bL_ops);
 16:         return 0;
 17: }
 18: 
 19: static struct platform_driver generic_bL_platdrv = {
 20:         .driver = {
 21:                 .name   = "arm-bL-cpufreq-dt",
 22:                 .owner  = THIS_MODULE,
 23:         },
 24:         .probe          = generic_bL_probe,
 25:         .remove         = generic_bL_remove,
 26: };
 27: module_platform_driver(generic_bL_platdrv);

kernel将arm big little cpufreq driver注册成了一个简单的platform driver,因此driver的入口就是其probe函数:generic_bL_probe。

注3:这里之所以把代码贴出,主要是看到“module_platform_driver”这个接口比较有趣。平时的经验,注册一个platform driver的固定格式是:定义一个platform driver变量,包含.probe()、.remove()等回调函数;定义两个函数,init和exit,在init函数中,调用platform_driver_register接口,注册该driver;使用module_init和module_exit声明init和exit函数。是不是很啰嗦?那就用这个接口吧,很省事!

1)generic_bL_probe

generic_bL_probe接口很简单,以dt_bL_ops为参数,调用bL_cpufreq_register接口,注册cpufreq driver。dt_bL_ops是一个struct cpufreq_arm_bL_ops类型的变量,提供两个回调函数,分别用于获取cluster切换之间的延迟,以及初始化opp table,后面用到的时候再介绍。

2)bL_cpufreq_register

bL_cpufreq_register位于“drivers/cpufreq/arm_big_little.c”中,主要负责如下事情:

a)执行一些初始化动作。

b)调用cpufreq_register_driver接口,注册名称为bL_cpufreq_driver的cpufreq driver。有关cpufreq driver以及cpufreq_register_driver的描述可参考“Linux cpufreq framework(2)_cpufreq driver”。

c)调用arm bL switcher driver提供的bL_switcher_register_notifier接口,向该driver注册一个notify,当bL switcher enable或者disable的时候,该driver会通知arm big little cpufreq driver,以完成相应的动作。

3)bL_cpufreq_driver

bL_cpufreq_driver代表了具体的cpufreq driver,提供了.init()、.verify()、.target_index()等回调函数,如下:

  1: static struct cpufreq_driver bL_cpufreq_driver = {
  2:         .name                   = "arm-big-little",
  3:         .flags                  = CPUFREQ_STICKY |
  4:                                         CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
  5:                                         CPUFREQ_NEED_INITIAL_FREQ_CHECK,
  6:         .verify                 = cpufreq_generic_frequency_table_verify,
  7:         .target_index           = bL_cpufreq_set_target,
  8:         .get                    = bL_cpufreq_get_rate,
  9:         .init                   = bL_cpufreq_init,
 10:         .exit                   = bL_cpufreq_exit,
 11:         .attr                   = cpufreq_generic_attr,
 12: };

有关cpufreq driver以及相关的回调函数已经在“Linux cpufreq framework(2)_cpufreq driver”中有详细介绍,这里再稍作说明一下:

.init()是cpufreq driver的入口函数,当该driver被注册到kernel中后,cpufreq core就会调用该回调函数,一般在init函数中初始化CPU core有关的frequency table,并依据该table填充相应的cpufreq policy变量。

.verify()可用于校验某个频率是否有效。

.target_index()可将CPU core设置为某一个频率,在本文的场景中,可以在修改频率是进行cluster切换,后面会详细介绍。

4)bL_cpufreq_init

bL_cpufreq_driver被注册后,cpufreq core就会调用bL_cpufreq_init接口,完成后续的初始化任务,该接口比较重要,是arm big little cpufreq driver的精髓,其定义如下:

  1: /* Per-CPU initialization */
  2: static int bL_cpufreq_init(struct cpufreq_policy *policy)
  3: {
  4:         u32 cur_cluster = cpu_to_cluster(policy->cpu);
  5:         struct device *cpu_dev;
  6:         int ret;
  7: 
  8:         cpu_dev = get_cpu_device(policy->cpu);
  9:         if (!cpu_dev) {
 10:                 pr_err("%s: failed to get cpu%d device\n", __func__,
 11:                                 policy->cpu);
 12:                 return -ENODEV;
 13:         }
 14: 
 15:         ret = get_cluster_clk_and_freq_table(cpu_dev);
 16:         if (ret)
 17:                 return ret;
 18: 
 19:         ret = cpufreq_table_validate_and_show(policy, freq_table[cur_cluster]);
 20:         if (ret) {
 21:                 dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n",
 22:                                 policy->cpu, cur_cluster);
 23:                 put_cluster_clk_and_freq_table(cpu_dev);
 24:                 return ret;
 25:         }
 26: 
 27:         if (cur_cluster < MAX_CLUSTERS) {
 28:                 int cpu;
 29: 
 30:                 cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
 31: 
 32:                 for_each_cpu(cpu, policy->cpus)
 33:                         per_cpu(physical_cluster, cpu) = cur_cluster;
 34:         } else {
 35:                 /* Assumption: during init, we are always running on A15 */
 36:                 per_cpu(physical_cluster, policy->cpu) = A15_CLUSTER;
 37:         }
 38: 
 39:         if (arm_bL_ops->get_transition_latency)
 40:                 policy->cpuinfo.transition_latency =
 41:                         arm_bL_ops->get_transition_latency(cpu_dev);
 42:         else
 43:                 policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
 44: 
 45:         if (is_bL_switching_enabled())
 46:                 per_cpu(cpu_last_req_freq, policy->cpu) = clk_get_cpu_rate(policy->cpu);
 47: 
 48:         dev_info(cpu_dev, "%s: CPU %d initialized\n", __func__, policy->cpu);
 49:         return 0;
 50: }

该接口根据当前bL switcher的使能情况(由cpu_to_cluster的返回值判断,如果等于MAX_CLUSTERS,bL switcher处于enable状态,否则,为disable状态),有两种截然不同行为。

bL switcher disable时(由于arm big little cpufreq driver先于arm bL switcher driver初始化,它初始化时,bL switcher处于disable状态):

  • 15行,调用get_cluster_clk_and_freq_table接口,为当前CPU所在的cluster创建frequency table,结果保存在freq_table[cluster]中
  • 27~33行,将和当前cpu以及同属于一个cluster的所有其它CPU都保存在policy->cpus中(具体意义请参考““Linux cpufreq framework(2)_cpufreq driver””),并将它们的physical_cluster设置为当前CPU的cluster
  • 以上逻辑的背后思路是:如果bL switcher没有enable,arm big little cpufreq driver就是一个普通的cpufreq driver,此时每个cluster的所有CPU(如4个A15 core,或者4个A7 core),共享同一个frequency table、cpufreq policy,也就是说,一个cluster下的所有CPU core,共享同一个调频策略

bL switcher enable时(会复杂一些):

  • 15行,调用get_cluster_clk_and_freq_table接口,搜集两个cluster下CPU的frequency信息,并以升序的形式合并到一个frequency table中(freq_table[MAX_CLUSTERS]),找出这些frequency中big core最小的那个(clk_big_min)以及Little core最大的那(clk_little_max)
  • 36行,将当前CPU的physical_cluster变量设置为当前A15_cluster,默认初始化时为big core模式
  • 以上逻辑背后的思路是:如果bL switcher enable,则所有的CPU core共用一个合并后的frequency table,并由一个调频策略统一调度,具体方法后面再详细介绍

bL_cpufreq_init的核心实现是get_cluster_clk_and_freq_table接口,该接口会根据bL switcher的使能情况,初始化不同的frequency table,并在bL switcher enable的时候,将不同cluster的frequency合并到一起。具体代码就不再详细分析,这里强调一下里面的一个小技巧:

为了让bL switcher逻辑顺利执行,有必要尽量准确的区分big core和Little core的频率,get_cluster_clk_and_freq_table使用了一个简单的方法:

对Little core来说,统一把frequency除以2,使用的时候再乘回来,这就基本上可以保证Little core的frequency位于合并后的频率表的前面位置,big core位于后面位置。

4.1.2 arm bL switcher driver的初始化

以ARM平台为例,arm bL switcher driver的初始化接口是bL_switcher_init,由于它使用late_init宏声明,因此会在靠后的时机初始化,该接口主要完成两个事情:

1)如果no_bL_switcher参数不为1(默认为0),则调用bL_switcher_enable接口,使能bL switcher。

2)调用bL_switcher_sysfs_init接口,初始化bL switcher模块提供的sysfs API,具体可参考3.2章节的介绍。

因此,arm bL switcher driver的初始化,就转移到bL_switcher_enable上面了。

4.2 enable/disable

bL switcher的使能与否,是由arm bL switcher driver控制的,以enable为例,enable的时机有两个:

1)arm bL switcher driver初始化的时候,调用bL_switcher_enable。

2)通过sysfs(/sys/kernel/bL_switcher/active)使能

4.2.1 bL_switcher_enable

bL_switcher_enable的实现如下:

  1: /* arch/arm/common/bL_switcher.c */
  2: static int bL_switcher_enable(void)
  3: {
  4:         int cpu, ret;
  5: 
  6:         mutex_lock(&bL_switcher_activation_lock);
  7:         lock_device_hotplug();
  8:         if (bL_switcher_active) {
  9:                 unlock_device_hotplug();
 10:                 mutex_unlock(&bL_switcher_activation_lock);
 11:                 return 0;
 12:         }
 13: 
 14:         pr_info("big.LITTLE switcher initializing\n");
 15: 
 16:         ret = bL_activation_notify(BL_NOTIFY_PRE_ENABLE);
 17:         if (ret)
 18:                 goto error;
 19: 
 20:         ret = bL_switcher_halve_cpus();
 21:         if (ret)
 22:                 goto error;
 23: 
 24:         bL_switcher_trace_trigger();
 25: 
 26:         for_each_online_cpu(cpu) {
 27:                 struct bL_thread *t = &bL_threads[cpu];
 28:                 spin_lock_init(&t->lock);
 29:                 init_waitqueue_head(&t->wq);
 30:                 init_completion(&t->started);
 31:                 t->wanted_cluster = -1;
 32:                 t->task = bL_switcher_thread_create(cpu, t);
 33:         }
 34: 
 35:         bL_switcher_active = 1;
 36:         bL_activation_notify(BL_NOTIFY_POST_ENABLE);
 37:         pr_info("big.LITTLE switcher initialized\n");
 38:         goto out;
 39: 
 40: error:
 41:         pr_warn("big.LITTLE switcher initialization failed\n");
 42:         bL_activation_notify(BL_NOTIFY_POST_DISABLE);
 43: 
 44: out:
 45:         unlock_device_hotplug();
 46:         mutex_unlock(&bL_switcher_activation_lock);
 47:         return ret;
 48: }

主要完成如下事情:

16行,调用bL_activation_notify,向arm big little cpufreq driver发送BL_NOTIFY_PRE_ENABLE通知,cpufreq driver收到该通知后,会调用cpufreq_unregister_driver,将bL_cpufreq_driver注销(具体可参考drivers/cpufreq/arm_big_little.c中的bL_cpufreq_switcher_notifier接口)。

20行,调用bL_switcher_halve_cpus接口,将系统所有possible的CPU core配对,并关闭不需要的core。该接口是本文的精髓,后面会稍微详细的介绍。

26~33行,为每个处于online状态的CPU core(此处已经是虚拟的core了,该core是一个big/Little对,同一时刻只有一个core开启),初始化用于cluster switch的线程。

35~36行,将bL_switcher_active置1,此时bL switcher正式enable了,向arm big little cpufreq driver发送BL_NOTIFY_POST_ENABLE通知,cpufreq driver会重新注册bL_cpufreq_driver。

4.2.2 bL_switcher_halve_cpus

bL_switcher_halve_cpus是ARM big·Little driver灵魂式的存在,它负责把系统8个big+Little core转化成4个虚拟的CPU core,例如,这8个core是这样排布的(以cpu的逻辑ID为索引):

01234567
LittleLittleLittleLittlebigbigbigbig

bL_switcher_halve_cpus会将不同cluster的core组成一对,最终的结果保存在bL_switcher_cpu_pairing数组中,形式如下:

bL_switcher_cpu_pairing[0] = 7 

bL_switcher_cpu_pairing[1] = 6

bL_switcher_cpu_pairing[2] = 5 

bL_switcher_cpu_pairing[3] = 4

配对之后,把core 4~7 disable掉,就保证系统当前只有4个虚拟的CPU core了。

4.2.3 bL_cpufreq_switcher_notifier

bL_switcher_enable前后,会通知arm big little cpufreq driver,该driver会把bL_cpufreq_driver注销之后再重新注册,整个过程又回到了“arm big little cpufreq driver的初始化”过程,具体可参考4.1.1。

4.3 cluster切换

最后,我们来看一下big core和Little core到底是怎么切换的。切换是由arm big little cpufreq driver的bL_cpufreq_set_target发起的,该接口的实现如下:

  1: static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
  2:                 unsigned int index)
  3: {
  4:         u32 cpu = policy->cpu, cur_cluster, new_cluster, actual_cluster;
  5:         unsigned int freqs_new;
  6: 
  7:         cur_cluster = cpu_to_cluster(cpu);
  8:         new_cluster = actual_cluster = per_cpu(physical_cluster, cpu);
  9: 
 10:         freqs_new = freq_table[cur_cluster][index].frequency;
 11: 
 12:         if (is_bL_switching_enabled()) {
 13:                 if ((actual_cluster == A15_CLUSTER) &&
 14:                                 (freqs_new < clk_big_min)) {
 15:                         new_cluster = A7_CLUSTER;
 16:                 } else if ((actual_cluster == A7_CLUSTER) &&
 17:                                 (freqs_new > clk_little_max)) {
 18:                         new_cluster = A15_CLUSTER;
 19:                 }
 20:         }
 21: 
 22:         return bL_cpufreq_set_rate(cpu, actual_cluster, new_cluster, freqs_new);
 23: }

切换的要点包括:

1)由4.2.2的描述可知,在bL switcher处于enable状态时,对调度器而言,只有4个core可见,而且这4个core的logical map是不变的,例如都是0、1、2、3。每一个core在物理上和2个属于不同cluster的core对应,同一时刻只有一个物理core运行

2)这4个core所处的“状态”(哪个物理core处于运行状态,big or Little),记录在“physical_cluster”中。

3)当经由cpufreq framework进行频率调整的时候,根据当前的“状态”,以及要调整的目的频率,计算是否需要切换cluster(也即disable当前正在运行的物理core,enable另外一个物理core)。

4)最终,以当前“状态”(actual_cluster)、新“状态”(new_cluster)等为参数,调用bL_cpufreq_set_rate接口,设置频率。

bL_cpufreq_set_rate接口经过一番处理后,得到真实的频率值,调用clock framework提供的接口(clk_set_rate)修改频率。之后,如果old_cluster和new_cluster不同,则调用arm bL switcher driver提供的bL_switch_request接口,进行cluster切换。该接口会启动一个线程,完成切换动作。该线程的处理函数是bL_switcher_thread,它会以目的cluster为参数,调用bL_switch_to接口,完成最终的切换操作。

bL_switch_to接口的实现比较复杂,需要迁移当前物理core的中断、timer,并disable当前物理core,然后enable目的cluster所代表的物理core,具体过程就不再详细分析了。

 

原创文章,转发请注明出处。蜗窝科技www.wowotech.net

<think>我们正在讨论的是PERF_RES_CPUFREQ_MIN_CLUSTER配置项,特别是针对不同簇(cluster)的配置,如CLUSTER_0, CLUSTER_1, CLUSTER_2。 根据引用[5]中的示例,我们看到在powerhint场景中使用了类似配置: <data cmd="PERF_RES_CPUFREQ_MIN_CLUSTER_0" param1="1500000"></data> <data cmd="PERF_RES_CPUFREQ_MIN_CLUSTER_1" param1="1800000"></data> 从命令字面可以拆解: - PERF_RES: 性能资源(Performance Resource) - CPUFREQ_MIN: CPU频率最小值 - CLUSTER_x: 指代第x个CPU簇(cluster) 因此,PERF_RES_CPUFREQ_MIN_CLUSTER_x配置项的作用是设置特定CPU簇的最低运行频率。 具体含义: - 该配置用于在特定场景(如触摸事件)下,提升CPU簇的最低频率,以保证流畅性。 - 参数param1的单位为kHz,例如1500000表示1.5GHz。 作用机制: - 当系统触发对应场景(如触摸)时,会通过此配置设置指定簇的scaling_min_freq,从而限制CPU频率不能低于设定值。 - 这样可以避免在需要响应速度的场景下,CPU频率过低导致卡顿。 在Linux内核中,CPU频率管理由cpufreq子系统负责,每个CPU簇(通常对应一个cpufreq policy)都有自己的频率范围。设置最小频率会影响该簇内所有CPU。 引用[1]中提到了cpufreq_quick_get(),它用于获取当前频率,而设置频率则通常通过scaling_min_freq和scaling_max_freq文件节点。 引用[5]中的配置示例: - 在触摸事件(APP_TOUCH)发生时,将cluster0的最小频率设置为1.5GHz,cluster1的最小频率设置为1.8GHz。 - 同时设置了其他参数(如CCI频率、DRAM OPP、调度器参数等)以协同提升性能。 注意:不同簇通常代表不同的CPU核心类型,例如: - CLUSTER_0: 小核(能效核) - CLUSTER_1: 大核(性能核) - CLUSTER_2: 超大核(如果有) 因此,配置项的作用是动态调整不同簇的CPU频率下限,以满足性能需求。 总结如下: - PERF_RES_CPUFREQ_MIN_CLUSTER_0: 设置小核簇的最低频率 - PERF_RES_CPUFREQ_MIN_CLUSTER_1: 设置大核簇的最低频率 - PERF_RES_CPUFREQ_MIN_CLUSTER_2: 设置超大核簇的最低频率(如果存在) 注意:配置值必须在该簇支持的频率范围内,否则会被限制到有效值。 数学表达: 设配置的最小频率为$f_{\text{min\_config}}$,实际生效的最小频率为: $$ f_{\text{min\_effective}} = \max(f_{\text{min\_config}}, f_{\text{min\_hardware}}) $$ 其中$f_{\text{min\_hardware}}$是硬件支持的最小频率。 同时,系统可能还有全局限制,因此实际生效的最小频率还受其他策略影响。 使用场景: 在需要提升响应速度的场景(如触摸、滑动、应用启动等)下,通过提高最低频率来避免CPU降频过低导致卡顿。 验证方法: 可以通过读取sysfs节点来确认配置是否生效: ```bash # 查看cluster0(假设对应cpu0-cpu3)的最小频率 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq ``` 注意事项: 1. 过高的最低频率可能导致功耗增加,需权衡性能与功耗。 2. 配置是动态的,只在特定场景下生效,场景结束后会恢复原设置。 相关问题: 1. 如何确定不同簇的物理CPU范围? 2. 设置最低频率与调度策略(如uclamp)如何协同工作? 3. 在哪些场景下需要调整这些配置? 4. 配置不生效的可能原因有哪些? 5. 如何监控配置的动态切换?</think>### PERF_RES_CPUFREQ_MIN_CLUSTER 配置详解 #### 1. **核心作用** 该配置项用于**动态调整CPU簇的最低运行频率**,在特定场景(如触摸响应)下提升系统响应速度。主要功能: - **性能保障**:防止CPU频率降至过低导致卡顿 - **场景优化**:针对不同负载需求动态调整频率下限 - **能效平衡**:在满足性能需求的前提下优化功耗[^5] #### 2. **参数解析** | **配置项** | **物理含义** | **典型值示例** | **单位** | |------------|--------------|----------------|----------| | `PERF_RES_CPUFREQ_MIN_CLUSTER_0` | 小核簇(能效核)最低频率 | 1500000 | kHz | | `PERF_RES_CPUFREQ_MIN_CLUSTER_1` | 大核簇(性能核)最低频率 | 1800000 | kHz | | `PERF_RES_CPUFREQ_MIN_CLUSTER_2` | 超大核簇(若有)最低频率 | 2200000 | kHz | #### 3. **技术原理** ```mermaid graph LR A[场景触发] --> B{如触摸事件} B --> C[设置CLUSTER_x最小频率] C --> D[cpufreq驱动更新策略] D --> E[CPU调度器限制] E --> F[频率不低于设定值] ``` 数学表达为: $$ f_{\text{actual}} = \max(f_{\text{min\_config}}, f_{\text{min\_hardware}}) $$ 其中: - $f_{\text{min\_config}}$ 是配置值(如1500000 kHz) - $f_{\text{min\_hardware}}$ 是硬件支持的最小频率 #### 4. **配置示例(引用[5])** ```xml <scenario powerhint="MTKPOWER_HINT_APP_TOUCH"> <data cmd="PERF_RES_CPUFREQ_MIN_CLUSTER_0" param1="1500000"></data> <data cmd="PERF_RES_CPUFREQ_MIN_CLUSTER_1" param1="1800000"></data> </scenario> ``` 表示: - 触摸事件发生时,强制小核簇频率≥1.5GHz - 强制大核簇频率≥1.8GHz - 配置持续生效直到触摸事件结束[^5] #### 5. **内核交互机制** 1. **配置生效**: ```c // 类似引用[1]中的cpufreq_quick_get() policy->min = config_value; // 更新策略最小频率 ``` 2. **调度器执行**: - 通过`cpufreq_driver->setpolicy`更新频率限制[^1] - 确保实际频率$f \geq f_{\text{min\_config}}$ 3. **硬件交互**: - 最终通过MSR或特定寄存器写入实现(如引用[3]的PMU操作) #### 6. **典型应用场景** | **场景** | 小核簇配置 | 大核簇配置 | 目的 | |----------|------------|------------|------| | 触摸响应 | ≥1.5GHz | ≥1.8GHz | 降低触控延迟 | | 游戏启动 | ≥1.2GHz | ≥2.0GHz | 避免加载卡顿 | | 视频解码 | ≥0.8GHz | ≥1.5GHz | 平衡流畅度与功耗 | | 待机状态 | 不配置 | 不配置 | 允许深度降频 | #### 7. **验证方法** ```bash # 查看Cluster 0当前最小频率 cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq # 监控配置生效日志 dmesg | grep -i "perf_res" ``` #### 8. **注意事项** 1. **层级限制**: - 实际频率 = max(配置值, 硬件下限, 温控限制) - 受`PERF_RES_CPUFREQ_MAX_THERMAL`等约束[^1] 2. **错误配置风险**: - 过高:导致不必要的功耗增加 - 过低:无法避免性能卡顿 3. **平台差异**: - ARM架构:Cluster_0通常为小核 - x86架构:可能对应P-core/E-core分组 --- ### 相关问题 1. **如何确定不同CPU簇的物理核心映射关系?** (需参考芯片文档,如ARMbig.LITTLE架构说明) 2. `PERF_RES_CPUFREQ_MIN_CLUSTER`与`PERF_RES_SCHED_UCLAMP_MIN`如何协同工作? (前者约束硬件频率,后者影响任务调度权重[^5]) 3. 配置不生效时的排查步骤有哪些? (检查:sysfs节点权限、thermal限制、cpufreq驱动状态) 4. 在动态调频场景中,该配置如何避免频率震荡? (通过hysteresis机制和采样周期设计[^1]) 5. 不同Android版本中这些配置的实现有何差异? (Linux内核cpufreq框架变更的影响)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值