cpu核数和内存大小怎么搭配 cpu核数( 三 )


static int boot_secondary(unsigned int cpu, struct task_struct *idle)
{
const struct cpu_operations *ops = get_cpu_ops(cpu);
if (ops->cpu_boot)
return ops->cpu_boot(cpu);
return -EOPNOTSUPP;
}
而通常在struct cpu_operations中的成员cpu_boot的执行中,会执行到secondary_entry –> secondary_startup –> __secondary_switched –> secondary_start_kernel 。
而在secondary_start_kernel中会调用set_cpu_online函数设置online位图 。
active这个位图的修改函数定义在CPU热插拔的回调函数数组中,也就是struct cpuhp_step cpuhp_hp_states[]中,该位图服务于调度器,在开启热插拔的情况下,调度器需要实时监控CPU热插拔的每个信息,因此需要该位图实时将某个CPU移除或添加 。
说完了四种位图,突然发现还没有说到CPU的层次结构在代码上的表示,CPU的层次结构使用一个结构体struct cpu_topology来表示 。
struct cpu_topology {
int thread_id;
int core_id;
int package_id;
int llc_id;
cpumask_t thread_sibling;
cpumask_t core_sibling;
cpumask_t llc_sibling;
};
#ifdef CONFIG_GENERIC_ARCH_TOPOLOGY
extern struct cpu_topology cpu_topology[NR_CPUS];
其实,代码上CPU的层次结构的赋值代码与CPU启动的时间基本一致,主CPU的赋值是在smp_prepare_cpus函数中;其余CPU是在secondary_start_kernel函数中 。
直接来看cpu_topology结构体的赋值函数store_cpu_topology 。在调用该函数之前,已经在init_cpu_topology函数中进行过cpu_topology数组的初始化了,其中thread_id、core_id、package_id和llc_id皆初始化为-1 。
void store_cpu_topology(unsigned int cpuid)
{
struct cpu_topology *cpuid_topo = &cpu_topology[cpuid];
u64 mpidr;
if (cpuid_topo->package_id != -1)
goto topology_populated;
mpidr = read_cpuid_mpidr();
/* Uniprocessor systems can rely on default topology values */
if (mpidr & MPIDR_UP_BITMASK)
return;
cpuid_topo->thread_id= -1;
cpuid_topo->core_id= cpuid;
cpuid_topo->package_id = cpu_to_node(cpuid);
pr_debug("CPU%u: cluster %d core %d thread %d mpidr %#016llx\\n",
cpuid, cpuid_topo->package_id, cpuid_topo->core_id,
cpuid_topo->thread_id, mpidr);
topology_populated:
update_siblings_masks(cpuid);
}
以arm64为例,thread_id应该是超线程情况下才有意义,这里不考虑 。core_id等同于物理核心id,也就是逻辑核心id,二者一致,package_id指向了其所属的NUMA的Node号 。update_sibling_masks则用于更新用于表示各个层级的兄弟关系 。
说了这么多,一直在说CPU的物理层次结构在代码上的表示,却还没有说到调度中CPU物理结构的参与 。
这里关注kernel_init_freezable函数有这样一段代码:
smp_init();
sched_init_smp();
前面已经了解到smp_init函数执行完成时,各个次CPU已经启动完成,此时进入sched_init_smp函数 。
sched_init_smp –> sched_init_domains –> build_sched_domains
static int
build_sched_domains(const struct cpumask *cpu_map, struct sched_domain_attr *attr)
{
enum s_alloc alloc_state = sa_none;
struct sched_domain *sd;
struct s_data d;
struct rq *rq = NULL;
int i, ret = -ENOMEM;
struct sched_domain_topology_level *tl_asym;
bool has_asym = false;
if (WARN_ON(cpumask_empty(cpu_map)))
goto error;
// 核心函数1,此时的cpu_map是active的位图
alloc_state = __visit_domain_allocation_hell(&d, cpu_map);
if (alloc_state != sa_rootdomain)
goto error;
tl_asym = asym_cpu_capacity_level(cpu_map);
/* Set up domains for CPUs specified by the cpu_map: */