void __init smp_prepare_cpus(unsigned int max_cpus) { int err; unsigned int cpu, ncores = num_possible_cpus();
init_cpu_topology(); 填充cpu_topology结构体数组
smp_store_cpu_info(smp_processor_id());
/* * are we trying to boot more cores than exist? */ if (max_cpus > ncores) 不能超过possible cpu数目 max_cpus = ncores;
/* Don't bother if we're effectively UP */ if (max_cpus <= 1) return;
/* * Initialise the present map (which describes the set of CPUs * actually populated at the present time) and release the * secondaries from the bootloader. * * Make sure we online at most (max_cpus - 1) additional CPUs. */ max_cpus--; for_each_possible_cpu(cpu) { if (max_cpus == 0) break;
if (cpu == smp_processor_id()) continue;
if (!cpu_ops[cpu]) continue;
err = cpu_ops[cpu]->cpu_prepare(cpu); 执行.cpu_prepare回调函数,将指定cpu设置为present。 if (err) continue;
set_cpu_present(cpu, true); max_cpus--; } }
cpu_boot
static int boot_secondary(unsigned int cpu, struct task_struct *idle) { if (cpu_ops[cpu]->cpu_boot) return cpu_ops[cpu]->cpu_boot(cpu);
return -EOPNOTSUPP; }
cpu_postboot
asmlinkage void secondary_start_kernel(void) 被汇编调用,作为secondary CPU的启动入口 { struct mm_struct *mm = &init_mm; unsigned int cpu = smp_processor_id();
/* * All kernel threads share the same mm context; grab a * reference and switch to it. */ atomic_inc(&mm->mm_count); current->active_mm = mm;
/* * TTBR0 is only used for the identity mapping at this stage. Make it * point to zero page to avoid speculatively fetching new entries. */ cpu_set_reserved_ttbr0(); local_flush_tlb_all(); cpu_set_default_tcr_t0sz();
preempt_disable(); trace_hardirqs_off();
/* * If the system has established the capabilities, make sure * this CPU ticks all of those. If it doesn't, the CPU will * fail to come online. */ verify_local_cpu_capabilities();
if (cpu_ops[cpu]->cpu_postboot) cpu_ops[cpu]->cpu_postboot();
/* * Log the CPU info before it is marked online and might get read. */ cpuinfo_store_cpu();
/* * Enable GIC and timers. */ notify_cpu_starting(cpu);
smp_store_cpu_info(cpu);
/* * OK, now it's safe to let the boot CPU continue. Wait for * the CPU migration code to notice that the CPU is online * before we continue. */ pr_info("CPU%u: Booted secondary processor [%08x]\n", cpu, read_cpuid_id()); set_cpu_online(cpu, true); 至此CPU可以设置为online状态 complete(&cpu_running);
/* * OK, it's off to the idle thread for us */ cpu_startup_entry(CPUHP_ONLINE); }
cpu_disable
static int op_cpu_disable(unsigned int cpu) { /* * If we don't have a cpu_die method, abort before we reach the point * of no return. CPU0 may not have an cpu_ops, so test for it. */ if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_die) return -EOPNOTSUPP;
/* * We may need to abort a hot unplug for some other mechanism-specific * reason. */ if (cpu_ops[cpu]->cpu_disable) return cpu_ops[cpu]->cpu_disable(cpu);
return 0; }
cpu_die
void cpu_die(void) { unsigned int cpu = smp_processor_id();
idle_task_exit();
local_irq_disable();
/* Tell __cpu_die() that this CPU is now safe to dispose of */ (void)cpu_report_death();
/* * Actually shutdown the CPU. This must never fail. The specific hotplug * mechanism must perform all required cache maintenance to ensure that * no dirty lines are lost in the process of shutting down the CPU. */ cpu_ops[cpu]->cpu_die(cpu);
BUG(); }
cpu_kill
static int op_cpu_kill(unsigned int cpu) { /* * If we have no means of synchronising with the dying CPU, then assume * that it is really dead. We can only wait for an arbitrary length of * time and hope that it's dead, so let's skip the wait and just hope. */ if (!cpu_ops[cpu]->cpu_kill) return 0;
static int __init arm_idle_init(void) { int cpu, ret; struct cpuidle_driver *drv = &arm_idle_driver; struct cpuidle_device *dev;
/* * Initialize idle states data, starting at index 1. * This driver is DT only, if no DT idle states are detected (ret == 0) * let the driver initialization fail accordingly since there is no * reason to initialize the idle driver if only wfi is supported. */ ret = dt_init_idle_driver(drv, arm_idle_state_match, 1); if (ret <= 0) return ret ? : -ENODEV;
ret = cpuidle_register_driver(drv); 注册arm_idle_driver驱动函数 if (ret) { pr_err("Failed to register cpuidle driver\n"); return ret; }
/* * Call arch CPU operations in order to initialize * idle states suspend back-end specific data */ for_each_possible_cpu(cpu) { ret = arm_cpuidle_init(cpu); 获取arch-specific的idle处理参数,这里对应cpu_psci_cpu_init_idle。
/* * Skip the cpuidle device initialization if the reported * failure is a HW misconfiguration/breakage (-ENXIO). */ if (ret == -ENXIO) continue;
if (ret) { pr_err("CPU %d failed to init idle CPU ops\n", cpu); goto out_fail; }
dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { pr_err("Failed to allocate cpuidle device\n"); goto out_fail; } dev->cpu = cpu;
ret = cpuidle_register_device(dev); if (ret) { pr_err("Failed to register cpuidle device for CPU %d\n", cpu); kfree(dev); goto out_fail; } }
return 0; out_fail: while (--cpu >= 0) { dev = per_cpu(cpuidle_devices, cpu); cpuidle_unregister_device(dev); kfree(dev); }
cpuidle_unregister_driver(drv);
return ret; }
arm_cpuidle_init调用.cpu_init_idle回调函数。
int __init arm_cpuidle_init(unsigned int cpu) { int ret = -EOPNOTSUPP;
if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_init_idle) ret = cpu_ops[cpu]->cpu_init_idle(cpu);
return ret; }
arm_enter_idle_state根据参数idx使CPU进入特定的idle状态,
static int arm_enter_idle_state(struct cpuidle_device *dev, struct cpuidle_driver *drv, int idx) { int ret;
if (!idx) { cpu_do_idle(); 如果idx为0,则cpu_do_idle。 return idx; }
ret = cpu_pm_enter(); if (!ret) { /* * Pass idle state index to cpu_suspend which in turn will * call the CPU ops suspend protocol with idle index as a * parameter. */ ret = arm_cpuidle_suspend(idx); 调用底层arch-specific处理函数。
cpu_pm_exit(); }
return ret ? -1 : idx; }
cpu_do_idle使CPU进入WFI状态。
ENTRY(cpu_do_idle) dsb sy // WFI may enter a low-power mode wfi ret ENDPROC(cpu_do_idle)
int arm_cpuidle_suspend(int index) { int cpu = smp_processor_id();
/* * If cpu_ops have not been registered or suspend * has not been initialized, cpu_suspend call fails early. */ if (!cpu_ops[cpu] || !cpu_ops[cpu]->cpu_suspend) return -EOPNOTSUPP; return cpu_ops[cpu]->cpu_suspend(index); }
static int cpu_psci_cpu_boot(unsigned int cpu) { int err = psci_ops.cpu_on(cpu_logical_map(cpu), __pa(secondary_entry)); if (err) pr_err("failed to boot CPU%d (%d)\n", cpu, err);
static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu) { int i, ret, count = 0; u32 *psci_states; struct device_node *state_node, *cpu_node;
cpu_node = of_get_cpu_node(cpu, NULL); if (!cpu_node) return -ENODEV;
/* * If the PSCI cpu_suspend function hook has not been initialized * idle states must not be enabled, so bail out */ if (!psci_ops.cpu_suspend) return -EOPNOTSUPP;
/* Count idle states */ while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", count))) { count++; of_node_put(state_node); }
if (!count) return -ENODEV;
psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); if (!psci_states) return -ENOMEM;
ret = of_property_read_u32(state_node, "arm,psci-suspend-param", &state); if (ret) { pr_warn(" * %s missing arm,psci-suspend-param property\n", state_node->full_name); of_node_put(state_node); goto free_mem; }
of_node_put(state_node); pr_debug("psci-power-state %#x index %d\n", state, i); if (!psci_power_state_is_valid(state)) { pr_warn("Invalid PSCI power state %#x\n", state); ret = -EINVAL; goto free_mem; } psci_states[i] = state; } /* Idle states parsed correctly, initialize per-cpu pointer */ per_cpu(psci_power_state, cpu) = psci_states; return 0;
free_mem: kfree(psci_states); return ret; }
1.解析DeviceTree中cpu下的cpu-idle-states属性
2.从每个state中获取arm,psci-suspend-param的参数,并验证是否有效。
3.初始化per-CPU类型的指针psci_power_state。
cpu_psci_cpu_suspend
static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index) { int ret; u32 *state = __this_cpu_read(psci_power_state); 从psci_power_state中读取suspend的state参数。 /* * idle state index 0 corresponds to wfi, should never be called * from the cpu_suspend operations */ if (WARN_ON_ONCE(!index)) return -EINVAL;
if (!psci_power_state_loses_context(state[index - 1])) ret = psci_ops.cpu_suspend(state[index - 1], 0); else ret = cpu_suspend(index, psci_suspend_finisher);
if (err) goto out_put_node; /* * Starting with v0.2, the PSCI specification introduced a call * (PSCI_VERSION) that allows probing the firmware version, so * that PSCI function IDs and version specific initialization * can be carried out according to the specific version reported * by firmware */ err = psci_probe();
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步