by 宋宝华
在《Tegra3 vSMP架构Android运行时CPU热插拔及高低功耗CPU切换》一文中我们通过运行华硕平板电脑看出来tegra3的dvfs结合了CPU热插拔和G/LP core cluster之间的切换,本文从代码角度分析其运行机理。
tegra3是典型的BIG/LITTLE模式,4个G core(高性能,高功耗)加上1个LP core(低性能,低功耗)。运行过程中,我们会发现4个G core会动态热插拔,而4个G core和1个LP core之间,会根据运行负载进行cluster切换。这一部分都是在内核里面实现,和tegra的cpufreq驱动(DVFS驱动)紧密关联。相关代码可见于
http://nv-tegra.nvidia.com/gitweb/?p=linux-2.6.git;a=tree;f=arch/arm/mach-tegra;h=e5d1ff22f5c5629f3c8078e1db308a4b45fa382e;hb=rel-14r7
相关代码未见于LKML和kernel org。
如何判断自己是什么core
每个core都可以通过调用is_lp_cluster()来判断当前执行CPU是LP还是G处理器:
static inline unsigned int is_lp_cluster(void) { unsignedint reg; reg =readl(FLOW_CTRL_CLUSTER_CONTROL); return (reg& 1); /* 0 == G, 1 == LP*/ }
即读FLOW_CTRL_CLUSTER_CONTROL寄存器判断出来自己是G core还是LP core。
G core和LP core cluster的切换时机
[场景1]何时从LP切换给G:当前执行于LPcluster,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:当前执行于Gcluster,cpufreq驱动判断出某G core需要降频率到小于低值门限,即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 cluster:
clk_set_parent(cpu_clk, cpu_lp_clk) ->
tegra3_cpu_cmplx_clk_set_parent(structclk *c, struct clk *p) ->
tegra_cluster_control(unsignedint us, unsigned int flags) ->
tegra_cluster_switch_prolog()-> tegra_cluster_switch_epilog()
G core动态热插拔
何时进行G core的动态plug和unplug:
[场景3]
1. 当前执行于G cluster,cpufreq驱动判断出某Gcore需要降频率到小于低值门限,即TEGRA_HP_DOWN,且最慢的CPUID小于nr_cpu_ids(实际上等价于还有2个或2个以上的G core活着),关闭最慢的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]
2. 当前执行于G cluster,cpufreq驱动判断出某Gcore需要设置频率大于高值门限,即TEGRA_HP_UP,如果负载平衡状态为TEGRA_CPU_SPEED_BALANCED,再开一个core;如果状态为TEGRA_CPU_SPEED_SKEWED,则关一个core。TEGRA_CPU_SPEED_BALANCED的含义是当前所有Gcore要求的频率都高于最高频率的50%,TEGRA_CPU_SPEED_SKEWED的含义是当前至少有2个Gcore要求的频率低于门限的25%,即CPU 频率的要求在各个core间有倾斜。
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; } }