[Linux][Power] SMP Bootcpu代码阅读记录

本文深入探讨Linux系统的CPU热插拔机制,包括CPU状态的转换流程,如在线、活动、存在及可能状态,以及系统如何通过调度和迁移任务来管理CPU资源。详细解析了从启动到在线状态的全过程,涉及的函数调用和关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

online	可以被调度的
active	可以被迁移的
present	内核已接管的
possible	系统存在的CPU,但没有被内核接管
cpu_down
	\->_cpu_down(cpu, 0)
		\->take_cpu_down
			\->__cpu_disable()
				\->mp_ops->cpu_disable()
        \->__cpu_die(cpu)

cpu_up
	\->_cpu_up
		\->__cpu_up
			\->mp_ops->boot_secondary(cpu, tidle)
start_kernel
    setup_arch
    arch_call_rest_init()--rest_init
        kernel_thread(kernel_init)
            kernel_init
                kernel_init_freeable
                    smp_prepare_cpus
                            	init_cpu_topology();
	                            this_cpu = smp_processor_id
	                            store_cpu_topology(this_cpu);
                                numa_store_cpu_info(this_cpu);
	                            numa_add_cpu(this_cpu);
	                            set_cpu_present(cpu, true);
		                        numa_store_cpu_info(cpu);
                    smp_init
                                idle_threads_init();            /*初始化各个cpu上的idle thread*/
	                            cpuhp_threads_init();           /*初始化各个cpu上的hotplug thread*/
	                                    smpboot_register_percpu_thread  /*smp thread 即是 cpuhp_threads--> cpuhp_thread_fun?*/
	                            cpu_up(cpu);
	                                    do_cpu_up(CPUHP_ONLINE)
	                                        try_online_node
	                                        _cpu_up
	                                                cpus_write_lock
	                                                idle_thread_get
	                                                cpuhp_set_state
	                                                cpuhp_kick_ap_work
	                                                        cpuhp_kick_ap
	                                                            __cpuhp_kick_ap(st);
	                                                                            smp_mb
	                                                                            wake_up_process(st-thread)
	                                                                            wait_for_ap_thread(st, st->bringup);
                                                                if ((ret = st->result)) {
		                                                            cpuhp_reset_state
		                                                            __cpuhp_kick_ap
	                                                           }
	                                                cpuhp_up_callbacks
	                                                        cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL);
	                                                                cbm(cpu, node)
	                           smp_cpus_done(setup_max_cpus);   /*Total of %d processors activated*/
	                                setup_cpu_features();
	                                hyp_mode_check();
	                                apply_alternatives_all();
	                                mark_linear_text_alias_ro();
        cpu_startup_entry()
            do_idle(CPU_ONLINE)
st-thread---------->cpuhp_thread_fun
    smp_mb
    cpuhp_lock_acquire
    cpuhp_invoke_callback
            hlist_for_each(node, &step->list) 
                cbm(cpu, node);
                        ******************
                        .bringup_cpu
                                __cpu_up
                                        secondary_data.task = idle;         /*设置起来的第一个task idle, stack, 关闭其MMU, 将dcache flush掉*/
	                                    secondary_data.stack = task_stack_page(idle) + THREAD_SIZE;
	                                    update_cpu_boot_status(CPU_MMU_OFF);
	                                    __flush_dcache_area(&secondary_data, sizeof(secondary_data));
	                                   boot_secondary
	                                        cpu_ops[cpu]->cpu_boot(cpu);
	                                            psci_ops.cpu_on(cpu_logical_map(cpu), __pa_symbol(secondary_entry));  /*从这里将回kernel的地址带下去, ENDPROC(secondary_entry)
定义在head.S里面*/
                                bringup_wait_for_ap
                                    wait_for_ap_thread  /*Wait for the CPU to reach CPUHP_AP_ONLINE_IDLE*/
                                    cpuhp_kick_ap
    cpuhp_lock_release
    complete_ap_thread
static struct cpuhp_step cpuhp_hp_states[] = {
	[CPUHP_OFFLINE] = {
		.name			= "offline",
		.startup.single		= NULL,
		.teardown.single	= NULL,
	},
#ifdef CONFIG_SMP
	[CPUHP_CREATE_THREADS]= {
		.name			= "threads:prepare",
		.startup.single		= smpboot_create_threads,
		.teardown.single	= NULL,
		.cant_stop		= true,
	},
	[CPUHP_PERF_PREPARE] = {
		.name			= "perf:prepare",
		.startup.single		= perf_event_init_cpu,
		.teardown.single	= perf_event_exit_cpu,
	},
	[CPUHP_WORKQUEUE_PREP] = {
		.name			= "workqueue:prepare",
		.startup.single		= workqueue_prepare_cpu,
		.teardown.single	= NULL,
	},
	[CPUHP_HRTIMERS_PREPARE] = {
		.name			= "hrtimers:prepare",
		.startup.single		= hrtimers_prepare_cpu,
		.teardown.single	= hrtimers_dead_cpu,
	},
	[CPUHP_SMPCFD_PREPARE] = {
		.name			= "smpcfd:prepare",
		.startup.single		= smpcfd_prepare_cpu,
		.teardown.single	= smpcfd_dead_cpu,
	},
	[CPUHP_RELAY_PREPARE] = {
		.name			= "relay:prepare",
		.startup.single		= relay_prepare_cpu,
		.teardown.single	= NULL,
	},
	[CPUHP_SLAB_PREPARE] = {
		.name			= "slab:prepare",
		.startup.single		= slab_prepare_cpu,
		.teardown.single	= slab_dead_cpu,
	},
	[CPUHP_RCUTREE_PREP] = {
		.name			= "RCU/tree:prepare",
		.startup.single		= rcutree_prepare_cpu,
		.teardown.single	= rcutree_dead_cpu,
	},
	/*
	 * On the tear-down path, timers_dead_cpu() must be invoked
	 * before blk_mq_queue_reinit_notify() from notify_dead(),
	 * otherwise a RCU stall occurs.
	 */
	[CPUHP_TIMERS_PREPARE] = {
		.name			= "timers:prepare",
		.startup.single		= timers_prepare_cpu,
		.teardown.single	= timers_dead_cpu,
	},
	/* Kicks the plugged cpu into life */
	[CPUHP_BRINGUP_CPU] = {
		.name			= "cpu:bringup",
		.startup.single		= bringup_cpu,
		.teardown.single	= NULL,
		.cant_stop		= true,
	},
	/* Final state before CPU kills itself */
	[CPUHP_AP_IDLE_DEAD] = {
		.name			= "idle:dead",
	},
	/*
	 * Last state before CPU enters the idle loop to die. Transient state
	 * for synchronization.
	 */
	[CPUHP_AP_OFFLINE] = {
		.name			= "ap:offline",
		.cant_stop		= true,
	},
	/* First state is scheduler control. Interrupts are disabled */
	[CPUHP_AP_SCHED_STARTING] = {
		.name			= "sched:starting",
		.startup.single		= sched_cpu_starting,
		.teardown.single	= sched_cpu_dying,
	},
	[CPUHP_AP_RCUTREE_DYING] = {
		.name			= "RCU/tree:dying",
		.startup.single		= NULL,
		.teardown.single	= rcutree_dying_cpu,
	},
	[CPUHP_AP_SMPCFD_DYING] = {
		.name			= "smpcfd:dying",
		.startup.single		= NULL,
		.teardown.single	= smpcfd_dying_cpu,
	},
	/* Entry state on starting. Interrupts enabled from here on. Transient
	 * state for synchronsization */
	[CPUHP_AP_ONLINE] = {
		.name			= "ap:online",
	},
	/*
	 * Handled on controll processor until the plugged processor manages
	 * this itself.
	 */
	[CPUHP_TEARDOWN_CPU] = {
		.name			= "cpu:teardown",
		.startup.single		= NULL,
		.teardown.single	= takedown_cpu,
		.cant_stop		= true,
	},
	/* Handle smpboot threads park/unpark */
	[CPUHP_AP_SMPBOOT_THREADS] = {
		.name			= "smpboot/threads:online",
		.startup.single		= smpboot_unpark_threads,
		.teardown.single	= smpboot_park_threads,
	},
	[CPUHP_AP_IRQ_AFFINITY_ONLINE] = {
		.name			= "irq/affinity:online",
		.startup.single		= irq_affinity_online_cpu,
		.teardown.single	= NULL,
	},
	[CPUHP_AP_PERF_ONLINE] = {
		.name			= "perf:online",
		.startup.single		= perf_event_init_cpu,
		.teardown.single	= perf_event_exit_cpu,
	},
	[CPUHP_AP_WATCHDOG_ONLINE] = {
		.name			= "lockup_detector:online",
		.startup.single		= lockup_detector_online_cpu,
		.teardown.single	= lockup_detector_offline_cpu,
	},
	[CPUHP_AP_WORKQUEUE_ONLINE] = {
		.name			= "workqueue:online",
		.startup.single		= workqueue_online_cpu,
		.teardown.single	= workqueue_offline_cpu,
	},
	[CPUHP_AP_RCUTREE_ONLINE] = {
		.name			= "RCU/tree:online",
		.startup.single		= rcutree_online_cpu,
		.teardown.single	= rcutree_offline_cpu,
	},
#endif
	/*
	 * The dynamically registered state space is here
	 */

#ifdef CONFIG_SMP
	/* Last state is scheduler control setting the cpu active */
	[CPUHP_AP_ACTIVE] = {
		.name			= "sched:active",
		.startup.single		= sched_cpu_activate,
		.teardown.single	= sched_cpu_deactivate,
	},
#endif

	/* CPU is fully up and running. */
	[CPUHP_ONLINE] = {
		.name			= "online",
		.startup.single		= NULL,
		.teardown.single	= NULL,
	},
};
在Zynq平台上配置和使用Linux SMP(对称多处理)涉及多个关键步骤,包括硬件设计、设备树配置、内核配置以及启动参数设置等。以下是一个详细的指南: ### 硬件设计 Zynq-7000系列SoC包含两个ARM Cortex-A9处理器核心,支持SMP架构。在硬件设计阶段,使用Xilinx Vivado工具创建硬件系统时,需要确保以下几点: - **CPU配置**:启用两个Cortex-A9核心,并确保它们被正确连接到共享内存和中断控制器。 - **内存控制器**:配置DDR控制器以支持多核访问共享内存。 - **中断控制器**:确保GIC(通用中断控制器)被正确配置,并且两个核心都能访问它[^1]。 ### 设备树配置 设备树(Device Tree)是Linux内核用来描述硬件配置的一种机制。为了启用SMP,设备树中必须包含两个CPU节点,并且它们的`enable-method`应设置为`spin-table`或`psci`(取决于内核版本和架构)。 ```dts / { cpus { #address-cells = <1>; #size-cells = <0>; cpu@0 { compatible = "arm,cortex-a9"; reg = <0>; clock-frequency = <666666666>; enable-method = "spin-table"; cpu0-supply = <&regulator_0>; }; cpu@1 { compatible = "arm,cortex-a9"; reg = <1>; clock-frequency = <666666666>; enable-method = "spin-table"; cpu1-supply = <&regulator_1>; }; }; clks { osc_clk: oscillator-clk { compatible = "fixed-clock"; #clock-cells = <0>; clock-frequency = <50000000>; }; }; }; ``` ### 内核配置 在编译Linux内核时,需要启用SMP支持。通常情况下,Xilinx提供的内核已经默认启用了SMP支持,但为了确认,可以在内核配置中检查以下选项: - `CONFIG_SMP=y` - `CONFIG_ARM_NEON=y`(可选,启用NEON指令集以提高性能) - `CONFIG_ARM_NEON_VFP=y`(可选,启用VFP支持) 此外,还需要确保以下选项被启用以支持多核调度和同步: - `CONFIG_SCHED_SMT=y` - `CONFIG_SCHED_MC=y` ### 启动参数设置 在U-Boot中,启动Linux内核时需要传递正确的启动参数。通常情况下,SMP支持不需要额外的启动参数,但为了确保多核正常工作,可以在U-Boot环境中设置以下变量: ```bash setenv bootargs 'console=ttyPS0,115200 root=/dev/mmcblk0p2 rw earlyprintk rootfstype=ext4' ``` 然后使用`bootm`命令加载并启动内核: ```bash bootm 0x8000 ``` ### 验证SMP是否启用 启动进入Linux系统后,可以通过以下命令验证SMP是否成功启用: ```bash cat /proc/cpuinfo ``` 如果SMP配置正确,输出应显示两个CPU核心的信息,例如: ``` processor : 0 model name : ARMv7 Processor rev 0 (v7l) BogoMIPS : 133.16 Features : swp half thumb fastmult vfp edsp neon vfpv3 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x2 CPU part : 0xc09 CPU revision : 0 processor : 1 model name : ARMv7 Processor rev 0 (v7l) BogoMIPS : 133.16 Features : swp half thumb fastmult vfp edsp neon vfpv3 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x2 CPU part : 0xc09 CPU revision : 0 ``` ### 性能调优 为了进一步优化SMP系统的性能,可以考虑以下几点: - **负载均衡**:确保内核的调度器能够有效地在两个核心之间分配任务。可以通过调整`/proc/sys/kernel/sched_mc_power_savings`和`/proc/sys/kernel/sched_smt_power_savings`来控制调度器的行为。 - **缓存一致性**:由于Cortex-A9核心具有独立的L1缓存,因此需要确保数据在多个核心之间保持一致。Linux内核会自动处理缓存一致性问题,但在某些情况下可能需要手动干预。 - **电源管理**:如果系统需要节能,可以启用CPU热插拔功能,动态地启用或禁用第二个核心。这可以通过`cpufreq`和`cpuidle`子系统实现。 ### 示例代码:多线程应用程序 为了测试SMP系统的性能,可以编写一个多线程应用程序,利用两个核心并行执行任务。以下是一个简单的多线程示例: ```c #include <stdio.h> #include <pthread.h> void* thread_func(void* arg) { int id = *(int*)arg; for (int i = 0; i < 5; i++) { printf("Thread %d: iteration %d\n", id, i); } return NULL; } int main() { pthread_t thread1, thread2; int id1 = 1, id2 = 2; pthread_create(&thread1, NULL, thread_func, &id1); pthread_create(&thread2, NULL, thread_func, &id2); pthread_join(thread1, NULL); pthread_join(thread2, NULL); return 0; } ``` 编译并运行该程序后,可以看到两个线程在不同的核心上并发执行。 ### 总结 通过上述步骤,可以在Zynq平台上成功配置和使用Linux SMP。从硬件设计到内核配置,再到启动参数设置和性能调优,每一步都至关重要。确保所有配置正确无误后,系统将能够充分利用双核Cortex-A9处理器的强大功能,提供更高的性能和更好的用户体验。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值