从start_kernel开始分析
init/main.c
start_kernel
|
smp_setup_processor_id
|
setup_arch
|
setup_nr_cpu_ids
|
setup_per_cpu_areas
|
smp_prepare_boot_cpu
|
boot_cpu_hotplug_init
|
arch_call_rest_init
smp_setup_processor_id, 以arm64为例:
arch/arm64/kernel/setup.c
void __init smp_setup_processor_id(void)
{
// 读取mpidr寄存器并通过掩码获取affinity值
u64 mpidr = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
//给boot cpu设置mpidr affinity,因为当前函数是初始化时调用的,所以cpu number一定是0
set_cpu_logical_map(0, mpidr);
pr_info("Booting Linux on physical CPU 0x%010lx [0x%08x]\n",
(unsigned long)mpidr, read_cpuid_id());
}
分析setup_arch
void __init __no_sanitize_address setup_arch(char **cmdline_p)
{
...
/* Parse the ACPI tables for possible boot-time configuration */
//初始化acpi 表
acpi_boot_table_init();
...
//给bootcpu,一般是cpu0, 初始化boot方式,从dt或者acpi表中获取,acpi的方法有psci和parking-protocol
init_bootcpu_ops();
//从dt或者acpi的madt表中获取cpu信息,填充逻辑cpu map并setup cpu
smp_init_cpus();
//压缩cpu mpidr的信息到mpidr_hash结构体中
smp_build_mpidr_hash();
...
}
分析smp_init_cpus
arch/arm64/kernel/smp.c
void __init smp_init_cpus(void)
{
int i;
//获取cpu信息并初始化cpu_logical_map
if (acpi_disabled)
of_parse_and_init_cpus();
else
acpi_parse_and_init_cpus();
...
//setup cpu,这里设置的cpu的数量是机器上已有的cpu数量,从dt或qcpi表中获取,和nr_cpus的较小的值
for (i = 1; i < nr_cpu_ids; i++) {
if (cpu_logical_map(i) != INVALID_HWID) {
if (smp_cpu_setup(i))
set_cpu_logical_map(i, INVALID_HWID);
}
}
}
cpu_logical_map是一个位图,index是cpu counter,从0到NR_CPUS,这个值是config_nr_cpus确定的,也就是编译时确定的。value是cpu的hwid,从dt或acpi的madt表中获取。初始化cpu_logical_map的路径是:acpi_parse_and_init_cpus->acpi_parse_gic_cpu_interface->acpi_map_gic_cpu_interface->set_cpu_logical_map
来看看acpi_map_gic_cpu_interface这个函数干了啥
static void __init
acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor)
{
//从madt表获得cpu mpidr信息
u64 hwid = processor->arm_mpidr;
//这个地方对于cpu hotplug有意义,对于还没有enable但是可以在之后enable的cpu可以在这里进行处理。可以认为acpi给未来插入的cpu留了位置
if (!(processor->flags & ACPI_MADT_ENABLED)) {
pr_debug("skipping disabled CPU entry with 0x%llx MPIDR\n", hwid);
return;
}
......
if (cpu_count >= NR_CPUS)
return;
//建立hwid和cpu counter的映射
set_cpu_logical_map(cpu_count, hwid);
cpu_madt_gicc[cpu_count] = *processor;
//跟AP(除boot cpu之外的cpu)启动有关
acpi_set_mailbox_entry(cpu_count, processor);
cpu_count++;
}
未完待续。。。
arch_call_rest_init差不多是start_kernel最后做的事情了。
arch_call_rest_init->rest_init->kernel_init
kernel_init是pid为1的线程.
kernel_init_freeable->smp_prepare_cpus