Kernel :4.12.8

arm64多核启动流程

smp_init_cpus() //设置多核启动参数和动作
=>static int __init smp_cpu_setup(int cpu) //位于 arch/arm64/kenerl/smp.c
    => cpu_read_ops
        => cpu_get_ops
        { ops = acpi_disabled ? dt_supported_cpu_ops : acpi_supported_cpu_ops; }

    .cpu_init    = smp_spin_table_cpu_init,

//分析从dts获取启动多核的两种方式 spin_table 和 psci
static const struct cpu_operations *dt_supported_cpu_ops[] __initconst = {
    &smp_spin_table_ops,
    &cpu_psci_ops,
    NULL,
};

// spin_table 方式的几个动作接口
const struct cpu_operations smp_spin_table_ops = {
    .name        = "spin-table",
    .cpu_init    = smp_spin_table_cpu_init,  
    .cpu_prepare    = smp_spin_table_cpu_prepare,
    .cpu_boot    = smp_spin_table_cpu_boot, //bring up实际动作
};


bringup_cpu
=>int __cpu_up(unsigned int cpu, struct task_struct *idle)
    =>ret = boot_secondary(cpu, idle);
        cpu_ops[cpu]->cpu_boot(cpu); //实际调用 smp_spin_table_cpu_boot 之类的启动

==============================
下面分析怎么执行cpu_boot的过程
==============================
static struct cpuhp_step cpuhp_bp_states[] =
    /* Kicks the plugged cpu into life */
    [CPUHP_BRINGUP_CPU] = {
        .name            = "cpu:bringup",
        .startup.single        = bringup_cpu,
        .teardown.single    = NULL,
        .cant_stop        = true,
    },

cpuhp_get_step() //唤起一个核有多步,这个是根据state选择对应的状态动作
    sp = cpuhp_is_ap_state(state) ? cpuhp_ap_states : cpuhp_bp_states;
    return sp + state;

 

kernel_init() =>kernel_init_freeable()

|=>void __init smp_prepare_cpus(unsigned int max_cpus)
    /*
     * Initialise the present map (which describes the set of CPUs
     * actually populated at the present time) and release the
     * secondaries from the bootloader.
     */
    for_each_possible_cpu(cpu) {
    err = cpu_ops[cpu]->cpu_prepare(cpu);
|=>smp_init => cpu_up=>do_cpu_up  // 同样是从kernel_init_freeable()调用下来的
    =>_cpu_up(unsigned int cpu, int tasks_frozen, enum cpuhp_state target)
       =>cpuhp_up_callbacks
        =>cpuhp_invoke_callback(unsigned int cpu, enum cpuhp_state state,
                 bool bringup, struct hlist_node *node)
        {
            if (!step->multi_instance) {
              cb = bringup ? step->startup.single : step->teardown.single;
              ret = cb(cpu);
            //这里实际就是拿出了bringup_cpu,调用之

        }

   
  spin table方式启动

实际就是把地址写入spin table中,然后发出sev (send envent,是一个指令)从核就奔跑起来了。

在smp_prepare_cpus() 中调用了smp_spin_table_cpu_prepare() 从核就开始跑了,不过内核设置了wfe 等待真正任务才能真正执行任务。

smp_init()中调用了smp_spin_table_cpu_boot()就是真正的启动了一个idle任务了(待确认)

static int smp_spin_table_cpu_prepare(unsigned int cpu)
{
	__le64 __iomem *release_addr;

	if (!cpu_release_addr[cpu])
		return -ENODEV;

	/*
	 * The cpu-release-addr may or may not be inside the linear mapping.
	 * As ioremap_cache will either give us a new mapping or reuse the
	 * existing linear mapping, we can use it to cover both cases. In
	 * either case the memory will be MT_NORMAL.
	 */
	release_addr = ioremap_cache(cpu_release_addr[cpu],
				     sizeof(*release_addr));
	if (!release_addr)
		return -ENOMEM;

	/*
	 * We write the release address as LE regardless of the native
	 * endianess of the kernel. Therefore, any boot-loaders that
	 * read this address need to convert this address to the
	 * boot-loader's endianess before jumping. This is mandated by
	 * the boot protocol.
	 */
	writeq_relaxed(__pa_symbol(secondary_holding_pen), release_addr);
	__flush_dcache_area((__force void *)release_addr,
			    sizeof(*release_addr));

	/*
	 * Send an event to wake up the secondary CPU.
	 */
	sev();

	iounmap(release_addr);

	return 0;
}

static int smp_spin_table_cpu_boot(unsigned int cpu)
{
	/*
	 * Update the pen release flag.
	 */
	write_pen_release(cpu_logical_map(cpu));

	/*
	 * Send an event, causing the secondaries to read pen_release.
	 */
	sev();

	return 0;
}

 

Logo

更多推荐