注:本篇所解析的fdt源描述附在文末
一、关于核状态
系统中CPU核一般在遍历时有四种状态,在本篇内容形状处有必要小小补充一下这部分的知识,
源码就不去分析了,按照上图所示的流程就能将每个核的四种状态补齐。
但我们应该知道,核是可以支持hotplug的,其中必然有些操作和这块东西扯上联系,可能的话后续单独补上这块内容。
二、核拓扑解析
关于核拓扑的解析过程主要入口在smp_prepare_cpus,那我们就以它为主攻点,层层击破。
void __init smp_prepare_cpus(unsigned int max_cpus)
{
...
init_cpu_topology(); ( 1 )
this_cpu = smp_processor_id();
store_cpu_topology(this_cpu); ( 2 )
numa_store_cpu_info(this_cpu); ( 3 )
numa_add_cpu(this_cpu); ( 4 )
if (max_cpus == 0) ( 5 )
return;
for_each_possible_cpu(cpu) { ( 6 )
per_cpu(cpu_number, cpu) = cpu;
if (cpu == smp_processor_id()) ( 7 )
continue;
ops = get_cpu_ops(cpu);
err = ops->cpu_prepare(cpu); ( 8 )
set_cpu_present(cpu, true); ( 9 )
numa_store_cpu_info(cpu); ( 10 )
}
}
1)拓扑解析的核心过程,在下面详细分析;
2)
3)TODO numa相关
4)TODO numa相关
5)
6)依次遍历每个已被系统检索到的核心;
7)
8)获取系统中配置的核配置操作集并调用cpu_prepare方法,操作集也是根据fdt中的参数解析出来的,这里配置为PSCI的方式,关于详细的PSCI操作分析,可见本站其它文章;
9)将当前核的状态设为present;
10)TODO numa相关
在init_cpu_topology中先会重置每个核对应的cpu_topology结构,然后调用parse_dt_topology针对核描述进一步解析,我们来看源码,
static int __init parse_dt_topology(void)
{
struct device_node *cn, *map;
cn = of_find_node_by_path("/cpus");
map = of_get_child_by_name(cn, "cpu-map"); ( 1 )
ret = parse_cluster(map, 0); ( 2 )
topology_normalize_cpu_scale(); ( 3 )
...
}
1)在fdt中检索出"/cpus"根节点,并在其中定位到"cpu-map"子节点,标准的平台都应该在此处将核拓扑描述清楚;
2)以"cluster"节点再次切入内部,继续进行层层解析,这一块逻辑注重fdt的解析,我们以一张图来概括;
3)计算综合算力,在下面进行分析;
拓扑解析parse_core中也包含算力解析,主要在topology_parse_cpu_capacity中实现,我们来看源码,
bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
{
...
ret = of_property_read_u32(cpu_node, "capacity-dmips-mhz",
&cpu_capacity); ( 1 )
if (!ret) {
if (!raw_capacity) {
raw_capacity = kcalloc(num_possible_cpus(),
sizeof(*raw_capacity),
GFP_KERNEL);
}
raw_capacity[cpu] = cpu_capacity; ( 2 )
pr_debug("cpu_capacity: %pOF cpu_capacity=%u (raw)\n",
cpu_node, raw_capacity[cpu]);
cpu_clk = of_clk_get(cpu_node, 0);
if (!PTR_ERR_OR_ZERO(cpu_clk)) {
per_cpu(freq_factor, cpu) =
clk_get_rate(cpu_clk) / 1000; ( 3 )
clk_put(cpu_clk);
}
}
...
}
1)解析核的"capacity-dmips-mhz"描述作为基础算力值;
2)将基础算力值转存到raw_capacity以备后面使用;
3)根据核节点中指定的时钟获取出对应的时钟频率;
void topology_normalize_cpu_scale(void)
{
u64 capacity;
u64 capacity_scale;
int cpu;
if (!raw_capacity)
return;
capacity_scale = 1;
for_each_possible_cpu(cpu) {
capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
capacity_scale = max(capacity, capacity_scale); ( 1 )
}
pr_debug("cpu_capacity: capacity_scale=%llu\n", capacity_scale);
for_each_possible_cpu(cpu) {
capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
capacity = div64_u64(capacity << SCHED_CAPACITY_SHIFT,
capacity_scale); ( 2 )
topology_set_cpu_scale(cpu, capacity); ( 3 )
pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
cpu, topology_get_cpu_scale(cpu));
}
}
1)通过遍历和计算得出最大的基础算力和对应频率的乘积转存到capacity_scale;
2)实际算力的最终计算公式可表示为
3)计算出实际算力并转存到cpu_scale,后续该值可能通过topology_get_cpu_scale接口获取到;
三、核拓扑及算力调试
附: 参考FDT
cpus {
#address-cells = <0x2>;
#size-cells = <0x0>;
cpu-map {
cluster0 {
core0 {
cpu = <0x2>;
};
core1 {
cpu = <0x3>;
};
core2 {
cpu = <0x4>;
};
core3 {
cpu = <0x5>;
};
};
cluster1 {
core0 {
cpu = <0x6>;
};
core1 {
cpu = <0x7>;
};
};
};
cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x0 0x0>;
enable-method = "psci";
capacity-dmips-mhz = <0x1e5>;
clocks = <0x8 0x8>;
#cooling-cells = <0x2>;
dynamic-power-coefficient = <0x64>;
cpu-idle-states = <0x9 0xa>;
operating-points-v2 = <0xb>;
cpu-supply = <0xc>;
phandle = <0x2>;
};
cpu@1 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x0 0x1>;
enable-method = "psci";
capacity-dmips-mhz = <0x1e5>;
clocks = <0x8 0x8>;
#cooling-cells = <0x2>;
dynamic-power-coefficient = <0x64>;
cpu-idle-states = <0x9 0xa>;
operating-points-v2 = <0xb>;
cpu-supply = <0xc>;
phandle = <0x3>;
};
cpu@2 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x0 0x2>;
enable-method = "psci";
capacity-dmips-mhz = <0x1e5>;
clocks = <0x8 0x8>;
#cooling-cells = <0x2>;
dynamic-power-coefficient = <0x64>;
cpu-idle-states = <0x9 0xa>;
operating-points-v2 = <0xb>;
cpu-supply = <0xc>;
phandle = <0x4>;
};
cpu@3 {
device_type = "cpu";
compatible = "arm,cortex-a53";
reg = <0x0 0x3>;
enable-method = "psci";
capacity-dmips-mhz = <0x1e5>;
clocks = <0x8 0x8>;
#cooling-cells = <0x2>;
dynamic-power-coefficient = <0x64>;
cpu-idle-states = <0x9 0xa>;
operating-points-v2 = <0xb>;
cpu-supply = <0xc>;
phandle = <0x5>;
};
cpu@100 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x0 0x100>;
enable-method = "psci";
capacity-dmips-mhz = <0x400>;
clocks = <0x8 0x9>;
#cooling-cells = <0x2>;
dynamic-power-coefficient = <0x1b4>;
cpu-idle-states = <0x9 0xa>;
operating-points-v2 = <0xd>;
cpu-supply = <0xe>;
phandle = <0x6>;
};
cpu@101 {
device_type = "cpu";
compatible = "arm,cortex-a72";
reg = <0x0 0x101>;
enable-method = "psci";
capacity-dmips-mhz = <0x400>;
clocks = <0x8 0x9>;
#cooling-cells = <0x2>;
dynamic-power-coefficient = <0x1b4>;
cpu-idle-states = <0x9 0xa>;
operating-points-v2 = <0xd>;
cpu-supply = <0xe>;
phandle = <0x7>;
};
idle-states {
entry-method = "psci";
cpu-sleep {
compatible = "arm,idle-state";
local-timer-stop;
arm,psci-suspend-param = <0x10000>;
entry-latency-us = <0x78>;
exit-latency-us = <0xfa>;
min-residency-us = <0x384>;
phandle = <0x9>;
};
cluster-sleep {
compatible = "arm,idle-state";
local-timer-stop;
arm,psci-suspend-param = <0x1010000>;
entry-latency-us = <0x190>;
exit-latency-us = <0x1f4>;
min-residency-us = <0x7d0>;
phandle = <0xa>;
};
};
};