注:本篇所解析的fdt源描述附在文末

一、关于核状态

系统中CPU核一般在遍历时有四种状态,在本篇内容形状处有必要小小补充一下这部分的知识,
image.png
源码就不去分析了,按照上图所示的流程就能将每个核的四种状态补齐。
但我们应该知道,核是可以支持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的解析,我们以一张图来概括;
image.png
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>;
        };
    };
};