第19章 Linux电源管理的系统架构和驱动之CPU热插拔

2019-07-14 01:43发布

19.8 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
# echo 1 > /sys/devices/system/cpu/cpu3/online通过echo 0 > /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(Variable Symmetric Multi Processing)架构,共有5个Cortex-A9处理器,其中4个为高性能设计的G核,1个为低功耗设计的LP核,如图19.7所示。
图19.7 Tegra3的架构
      在系统运行过程中,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); //读FLOW_CTRL_CLUSTER_CONTROL寄存器判断自己是G核还是LP核
        return (reg & 1); /* 0 == G, 1 == LP */
}2.G核和LP核集群的切换时机
[场景1]何时从LP核切换给G核:当前执行于LP集群,CPUFreq驱动判断出LP核需要增频率到超过高值门限,即TEGRA_HP_UP。
caseTEGRA_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。
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集群。
3.G核动态热插拔
何时进行G核的动态插拔,具体如下。
[场景3]当前执行于G集群,CPUFreq驱动判断出某G核需要降频率到低于低值门限,即TEGRA_HP_DOWN,且最慢的CPUID小于nr_cpu_ids,关闭最慢的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                                 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;
               }
        }目前,ARM和Linux社区都在从事关于big.LITTLE架构下,CPU热插拔以及调度器方面有针对性的改进工作。在big.LITTLE架构中,将高性能且功耗也较高的Cortex-A15稍低性能且功耗低的Cortex-A7进行结合,或者在64位下,进行Cortex-A57和Cortex-A53的组合,如图19.8所示。

图19.8 ARM的big.LITTLE架构
big.LITTLE架构的设计旨在为适当的作业分配恰当的处理器。Cortex-A15处理器是目前已开发的性能最高的低功耗ARM处理器,而Cortex-A7处理器是目前已开发的最节能的ARM应用程序处理器。可以利用Cortex-A15处理器的性能来承担繁重的工作负载,而用Cortex-A7可以最有效地处理智能手机的大部分工作负载。这些操作包括操作系统活动、用户界面和其他持续运行、始终连接的任务。