欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

驱动篇:inux 电源管理的系统架构和驱动(四)

程序员文章站 2024-02-24 09:28:49
...

驱动篇:inux 电源管理的系统架构和驱动(四)

CPU 热插拔
Linux CPU 热插拔的功能已经存在相当长的时间了, Linux 3.8 之后的内核里一个小小的改进就是 CPU0 也可以热插拔。
一般来讲,在用户空间可以通过 /sys/devices/system/cpu/cpun/online 节点来操作一个 CPU 的在线和离线:

# echo 0>/sys/devices/system/cpu/cpu3/online
CPU 3 is now offline
# echo1 >/sys/devices/system/cpu/cpu3/online

通过 echo0>/sys/devices/system/cpu/cpu3/online 关闭 CPU3 的时候, CPU3 上的进程都会被迁移到其他的 CPU 上,以保证这个拔除 CPU3 的过程中,系统仍然能正常运行。一旦通过 echo 1>/sys/devices/system/cpu/cpu3/online 再次开启 CPU3 , CPU3 又可以参与系统的负载均衡,分担系统中的任务。
在嵌入式系统中, CPU 热插拔可以作为一种省电的方式,在系统负载小的时候,动态关闭 CPU ,在系统负载增大的时候,再开启之前离线的 CPU 。目前各个芯片公司可能会根据自身 SoC 的特点,对内核进行调整,来实现运行时 “ 热插拔 ” 。这里以 Nvidia 的 Tegra3 为例进行说明。

Tegra3 采用 vSMP ( variableSymmetric Multiprocessing )架构,共有 5 个 Cortex-A9 处理器,其中 4 个为高性能设计的G 核, 1 个为低功耗设计的 LP 核,如图所示:
驱动篇:inux 电源管理的系统架构和驱动(四)在系统运行过程中, Tegra3 的 Linux 内核会根据 CPU 负载切换低功耗处理器和高功耗处理器。除此之外, 4 个高性能 ARM 核心也会根据运行情况,动态借用 Linux 内核支持的 CPU 热插拔进行 CPU 的插入 / 拔出操作。
用华硕 EeePad 运行高负载、低负载应用,通过 dmesg 查看内核消息也确实验证了多核的热插拔以及 G 核和 LP 核之间的动态切换:

<4>[104626.426957] CPU1: Booted secondary processor
<7>[104627.427412] tegra CPU: force EDP limit 720000 kHz<4>[104627.427670] CPU2: Booted secondary processor
<4>[104628.537005] stop_machine_cpu_stop cpu=0
<4>[104628.537017] stop_machine_cpu_stop cpu=2
<4>[104628.537059] stop_machine_cpu_stop cpu=1
<4>[104628.537702] __stop_cpus: wait_for_completion_timeout+
<4>[104628.537810] __stop_cpus: smp=0 done.executed=1 done.ret =0-
<5>[104628.537960] CPU1: clean shutdown
<4>[104630.537092] stop_machine_cpu_stop cpu=0
<4>[104630.537172] stop_machine_cpu_stop cpu=2
<4>[104630.537739] __stop_cpus: wait_for_completion_timeout+
<4>[104630.538060] __stop_cpus: smp=0 done.executed=1 done.ret =0-
<5>[104630.538203] CPU2: clean shutdown
<4>[104631.306984] tegra_watchdog_touch

高性能处理器和低功耗处理器切换:

<3>[104666.799152] LP=>G: prolog 22 us, switch 2129 us, epilog 24 us, total 2175 us
<3>[104667.807273] G=>LP: prolog 18 us, switch 157 us, epilog 25 us, total 200 us
<4>[104671.407008] tegra_watchdog_touch
<4>[104671.408816] nct1008_get_temp: ret temp=35C
<3>[104671.939060] LP=>G: prolog 17 us, switch 2127 us, epilog 22 us, total 2166 us
<3>[104672.938091] G=>LP: prolog 18 us, switch 156 us, epilog 24 us, total 198 us

在运行过程中,我们发现 4 个 G 核会动态热插拔,而 4 个 G 核和 1 个 LP 核之间,会根据运行负载进行集群切换。这一部分都是在内核里面实现的,和 tegra 的 CPUF req 驱动( DVFS 驱动)紧密关联。

1 . 如何判断自己是什么核
每个核都可以通过调用 is_lp_cluster ()来判断当前正在执行的 CPU 是 LP 还是 G 处理器:

static inline unsigned int is_lp_cluster(void)
{
unsigned int reg;
reg =readl(FLOW_CTRL_CLUSTER_CONTROL);
return (reg& 1); /* 0 == G, 1 == LP */
}

即读 FLOW_CTRL_CLUSTER_CONTROL 寄存器判断自己是 G 核还是 LP 核。

2.G 核和 LP 核集群的切换时机
[场景 1 ]何时从 LP 核切换给 G 核:当前执行于 LP 集群, CPUFreq 驱动判断出 LP 核需要增频率到超过高值门限,即 TEGRA_HP_UP :

case TEGRA_HP_UP:
if(is_lp_cluster() && !no_lp) {
if(!clk_set_parent(cpu_clk, cpu_g_clk)) {
hp_stats_update(CONFIG_NR_CPUS, false);
hp_stats_update(0, true);
/* catch-upwith governor target speed */
tegra_cpu_set_speed_cap(NULL);
}
}    

[场景 2 ]何时从 G 核切换给 LP 核:当前执行于 G 集群, CPUFreq 驱动判断出某 G 核需要降频率到低于低值门限,即 TEGRA_HP_DOWN ,且最慢的 CPUID 不小于 nr_cpu_ids (实际上代码逻辑跟踪等价于只有 CPU0 还活着的情况):

caseTEGRA_HP_DOWN:
cpu= tegra_get_slowest_cpu_n();
if(cpu < nr_cpu_ids) {
...
}else if(!is_lp_cluster() && !no_lp) {
if(!clk_set_parent(cpu_clk, cpu_lp_clk)) {
hp_stats_update(CONFIG_NR_CPUS, true);
hp_stats_update(0, false);
/* catch-upwith governor target speed */
tegra_cpu_set_speed_cap(NULL);
} else
queue_delayed_work(
hotplug_wq, &hotplug_work, down_delay);
}
break;

切换实际上就发生在 clk_set_parent ()更改 CPU 的父时钟里面,这部分代码写得比较不好, 1 个函数完成 n 个功能,实际上不仅切换了时钟,还切换了 G 和 LP 集群:

clk_set_parent(cpu_clk, cpu_lp_clk) ->
tegra3_cpu_cmplx_clk_set_parent(structclk *c, struct clk *p) ->
tegra_cluster_control(unsigned int us, unsigned int flags) ->
tegra_cluster_switch_prolog()->
tegra_cluster_switch_epilog()

3.G 核动态热插拔
何时进行 G 核的动态插拔,具体如下。
[场景 3 ]当前执行于 G 集群, CPUFreq 驱动判断出某 G 核需要降频率到低于低值门限,即 TEGRA_HP_DOWN ,且最慢的 CPUID 小于 nr_cpu_ids (实际上等价于还有两个或两个以上的 G 核活着的情况),关闭最慢的 CPU ,留意 tegra_get_slowest_cpu_n ()不会返回 0 ,这意味着 CPU0 要么活着,要么切换给了 LP 核,对应于[场景 2 ]:

caseTEGRA_HP_DOWN:
cpu= tegra_get_slowest_cpu_n();
if(cpu < nr_cpu_ids) {
up = false;
queue_delayed_work(
hotplug_wq,&hotplug_work, down_delay);
hp_stats_update(cpu, false);
}

[场景 4 ]当前执行于 G 集群, CPUFreq 驱动判断出某 G 核需要设置频率大于高值门限,即TEGRA_HP_UP ,如果负载平衡状态为 TEGRA_CPU_SPEED_BALANCED ,再开一个核;如果状态为TEGRA_CPU_SPEED_SKEWED ,则关一个核。 TEGRA_CPU_SPEED_BALANCED 的含义是当前所有 G 核要求的频率都高于最高频率的 50% , TEGRA_CPU_SPEED_SKEWED 的含义是当前至少有两个 G 核要求的频率低于门限的 25% ,即 CPU 频率的要求在各个核之间有倾斜。

caseTEGRA_HP_UP:
if(is_lp_cluster() && !no_lp) {
...
}else {
switch (tegra_cpu_speed_balance()) {
/* cpu speed is up and balanced - one more on-line */
case TEGRA_CPU_SPEED_BALANCED:
cpu =cpumask_next_zero(0, cpu_online_mask);
if(cpu <nr_cpu_ids) {
up =true;hp_stats_update(cpu, true);
}
break;
/* cpu speed is up, but skewed - remove one core */
case TEGRA_CPU_SPEED_SKEWED:
cpu =tegra_get_slowest_cpu_n();
if(cpu < nr_cpu_ids) {
up =false;
hp_stats_update(cpu, false);
}
break;
/* cpu speed is up, butunder-utilized - do nothing */
case TEGRA_CPU_SPEED_BIASED:
default:
break;
}
}

上述代码中 TEGRA_CPU_SPEED_BIASED 路径的含义是有 1 个以上 G 核的频率低于最高频率的 50% 但是还未形成 “SKEWED” 条件,即只是 “BIASED” ,还没有达到 “SKEWED” 的程度,因此暂时什么都不做。
目前, ARM 和 Linux 社区都在从事关于 big.LITTLE 架构下, CPU 热插拔以及调度器方面有针对性的改进工作。在big.LITTLE 架构中,将高性能且功耗也较高的 Cortex-A15 和稍低性能且功耗低的 Cortex-A7 进行了结合,或者在 64位下,进行 Cortex-A57 和 Cortex-A53 的组合,如图 19.8 所示。
big.LITTLE 架构的设计旨在为适当的作业分配恰当的处理器。 Cortex-A15 处理器是目前已开发的性能最高的低功耗 ARM 处理器,而 Cortex-A7 处理器是目前已开发的最节能的 ARM 应用程序处理器。可以利用 Cortex-A15 处理器的性能来承担繁重的工作负载,而用 Cortex-A7 可以最有效地处理智能手机的大部分工作负载。这些操作包括操作系统活动、用户界面和其他持续运行、始终连接的任务。
驱动篇:inux 电源管理的系统架构和驱动(四)

相关标签: 内核 电源管理