online/offline使用
比如,我们可以通过sysfs把一个核下线,写1又可以把这个核上线。
1
| echo 0 > /sys/devices/system/cpu/cpu3/online
|
一个核被下线,相当于把这个core关掉,从新上线的核从初始状态开始运行。
实现逻辑
可见内核把CPU核也当作一种设备管理,那么就有核对应的设备、总线和驱动。内核定义了
cpu bus: struct bus_type cpu_subsys,一个cpu用struct cpu表示,
对固件的需求
kernel/sched/idle.c
1 2 3 4 5 6 7 8 9
| do_idle +-> arch_cpu_idle_dead +-> cpu_ops[]->cpu_stop()
play_idle_precise +-> do_idle
cpu_startup_entry +-> do_idle
|
cpu device
1 2 3 4 5 6 7 8 9 10
| driver_init /* drivers/base/cpu.c */ +-> cpu_dev_init /* * 注册cpu的sysfs接口,online/offline的入口就在这里: struct bus_type cpu_subsys * 的回调函数中: .online/.offline。 */ +-> subsys_system_register(&cpu_subsys, cpu_root_attr_groups) +-> cpu_dev_register_generic +-> register_cpu
|
1 2 3 4 5 6 7 8 9 10
| cpu_subsys_online +-> cpu_device_up ... +-> _cpu_up +-> cpuhp_up_callbacks ... /* 循环调用cpuhp_state的回调 */ +-> __cpuhp_invoke_callback_range /* 每次调用这个,可以看到每次回调前后都有trace点 */ +-> cpuhp_invoke_callback
|
所有的cpuhp_state定义在include/linux/cpuhotplug.h,每个具体的cpuhp_state的cpuhp_step
定义在kernel/cpu.c: cpuhp_hp_states,其中的CPUHP_BRINGUP_CPU的回调为bringup_cpu。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| bringup_cpu /* arch/arm64/kernel/smp.c */ +-> __cpu_up +-> boot_secondary /* * 回调定义在arch/arm64/kernel/psci.c: cpu_psci_cpu_boot。注意,这个 * 函数把cpu编号转成mpidr,这个转换关系是提前解析,然后保存在 * __cpu_logical_map这个表里。CPU核的mpidr是从ACPI MADT表的GICC mpidr * 域段解析到的,解析的具体路径为: (NUMA node的解析也在这里) * * smp_init_cpus->acpi_parse_and_init_cpus->acpi_table_parse_madt */ +-> ops->cpu_boot /* 不同版本的psci_ops定义在: drivers/firmware/psci/psci.c */ +-> psci_ops.cpu_on /* * 以0.2版本为例。 * * 根据FADT.arm_boot_flags决定是用SMC还是用HVC(在psci_acpi_init())。 * psci_acpi_init->psci_probe中探测psci版本。 */ +-> psci_0_2_cpu_on
/* 等待online的CPU运行secondary_start_kernel,其中会解开这里的等待 */ +-> wait_for_completion_timeout(&cpu_running, xxx) +-> if (cpu_online(cpu)) return 0; ...
|
以SMC为例,展开psci_0_2_cpu_on的逻辑:
1 2 3 4 5 6 7 8 9 10
| psci_0_2_cpu_on +-> __psci_cpu_on(PSCI_FN_NATIVE(0_2, CPU_ON), cpuid, entry_point) /* function_id为PSCI_0_2_FN64_CPU_ON */ +-> invoke_psci_fn(fn, cpuid, entry_point, 0) +-> arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res) /* arch/arm64/kernel/smccc-call.S,smc为核心的一断汇编在这里 */ +-> __arm_smccc_smc
/* 虚机里执行smc,trap到KVM一路执行到arch/arm64/kvm/psci.c如下函数 */ +-> kvm_psci_vcpu_on(vcpu)
|
kvm_psci_vcpu_on reset vCPU,把vCPU线程投入执行。