0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

CPU 拓扑中的SMP架构

openEuler 来源:openEuler 作者:openEuler 2022-08-29 11:02 次阅读

CPU 拓扑用来表示 CPU 在硬件层面的组合方式,本文主要讲解 CPU 拓扑中的 SMP(Symmetric Multi-Processor,对称多处理器系统)架构,CPU 拓扑还包括其他信息,比如:cache 等,这些部分会在后面进行补充。CPU 拓扑除了描述 CPU 的组成关系外,还为内核的调度器提供服务,从而提供更好的性能。在 StratoVirt 中,支持 CPU 拓扑为后续的 CPU 热插拔开发打下一个基础。

常见的 CPU SMP 结构是:

Socket-->die-->cluster-->core-->thread

socket:对应主板上的 CPU 插槽

die:处理器在生产过程中,从晶圆上切割下来的一个个小方块,Die 之间的组件是通过片内总线互联的。

cluster:簇,大核或者小核的一种组合

core:表示独立的物理 CPU

thread:逻辑 CPU,英特尔超线程技术引入的新概念

CPU 拓扑的获取原理

因为 x86 和 ARM 的拓扑获取方式不同,下面将会分开进行介绍。

x86

在 x86 架构下面,操作系统会通过读取 CPUID 来获取 CPU 拓扑结构。在 x86 体系结构中,CPUID 指令(由 CPUID 操作码标识)是处理器补充指令(其名称源自 CPU 标识),允许软件发现处理器的细节。程序可以使用 CPUID 来确定处理器类型。

CPUID 隐式使用 EAX 寄存器来确定返回的信息的主要类别,这被称为 CPUID 叶。跟 CPU 拓扑相关的 CPUID 叶分别是:0BH 和 1FH。1FH 是 0BH 的扩展,可以用来表示更多的层级。Intel 建议先检查 1FH 是否存在,如果 1FH 存在会优先使用它。当 EAX 的值被初始化为 0BH 的时候,CPUID 会在 EAX,EBX,ECX 和 EDX 寄存器中返回 core/logical 处理器拓扑信息。这个函数(EAX=0BH)要求 ECX 同时被初始化为一个 index,这个 index 表示的是在 core 层级还是 logical processor 层级。OS 调用这个函数是按 ECX=0,1,2..n 这个顺序调用的。返回处理器拓扑级别的顺序是特定的,因为每个级别报告一些累积数据,因此一些信息依赖于从先前级别检索到的信息。在 0BH 下,ECX 可以表示的层级有:SMT 和 Core,在 1FH 下,可以表示的层级有:SMT,Core,Module,Tile 和 Die。

下表是一个更详细的一个解释:

Initial EAX Value Information Provided about the Processor
0BH EAX Bits 04 - 00: Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level type*. All logical processors with the same next level ID share current level. Bits 31 - 05: Reserved. EBX Bits 15 - 00: Number of logical processors at this level type. The number reflects configuration as shipped by Intel. Bits 31- 16: Reserved. ECX Bits 07 - 00: Level number. Same value in ECX input. Bits 15 - 08: Level type. Bits 31 - 16: Reserved. EDX Bits 31- 00: x2APIC ID the current logical processor.
1FH EAX Bits 04 - 00: Number of bits to shift right on x2APIC ID to get a unique topology ID of the next level type*. All logical processors with the same next level ID share current level. Bits 31 - 05: Reserved. EBX Bits 15 - 00: Number of logical processors at this level type. The number reflects configuration as shipped by Intel. Bits 31- 16: Reserved. ECX Bits 07 - 00: Level number. Same value in ECX input. Bits 15 - 08: Level type. Bits 31 - 16: Reserved. EDX Bits 31- 00: x2APIC ID the current logical processor

来源: Intel 64 and IA-32 Architectures Software Developer's Manual

ARM

在 ARM 架构下,如果操作系统是依靠 Device Tree 启动的,则会通过 Device Tree 去获取 CPU 拓扑。如果是以 ACPI 的方式启动的话,操作系统会通过解析 ACPI 的 PPTT 表去获取 CPU 拓扑结构。

ACPI——PPTT

ACPI 是 Advanced Configuration and Power Interface (高级配置和电源接口)的缩写,ACPI 是一种与体系结构无关的电源管理和配置框架。这个框架建立了一个硬件寄存器集合来定义电源状态。ACPI 是操作系统和固件之间的一个中间层,是他们两者之间的一个接口。ACPI 定义了两种数据结构:data tables 和 definition blocks。data tables 用于存储给设备驱动使用的 raw data。definition blocks 由一些字节码组成,这些码可以被解释器执行。

为了使硬件供应商在选择其实施时具有灵活性,ACPI 使用表格来描述系统信息、功能和控制这些功能的方法。这些表列出了系统主板上的设备或无法使用其他硬件标准检测或电源管理的设备,以及 ACPI 概念中所述的功能。它们还列出了系统功能,如支持的睡眠电源状态、系统中可用的电源平面和时钟源的说明、电池、系统指示灯等。这使 OSPM 能够控制系统设备,而不需要知道系统控制是如何实现的。

PPTT 表就是其中的一个表格,PPTT 表全称是 Processor Properties Topology Table,处理器属性拓扑表用于描述处理器的拓扑结构,该表还可以描述附加信息,例如处理器拓扑中的哪些节点构成物理包。

下表是 PPTT 表的结构,包含一个表头和主体,表头和其他的 ACPI 表差别不大。其中 Signature 用于表示这是 PPTT 表,Length 是整张表的大小,其他的信息可以查看下面的这张表。表的主体是一系列处理器拓扑结构。

5c7a9efc-261e-11ed-ba43-dac502259ad0.png

下面的表表示处理器层级节点结构,表示处理器结构的话 Type 要设置为 0,Length 表示这个节点的字节数。Flags 用来描述跟处理器相关的信息,详细的看后面关于 Flags 的详细信息。Parent 用于指向这个节点的上一级节点,存放的是一个偏移量地址

5ca24358-261e-11ed-ba43-dac502259ad0.png

下表是 Flags 的结构,Flags 占据 4 个字节的长度。Physical package:如果处理器拓扑的此节点表示物理封装的边界,则设置 Physical package 为 1。如果处理器拓扑的此实例不表示物理软件包的边界,则设置为 0。Processor is a Thread:对于叶条目:如果代表此处理器的处理元素与兄弟节点共享功能单元,则必须将其设置为 1。对于非叶条目:必须设置为 0。Node is a Leaf:如果节点是处理器层次结构中的叶,则必须设置为 1。否则必须设置为 0。

5cc3e670-261e-11ed-ba43-dac502259ad0.png

参考:https://uefi.org/specs/ACPI/6.4/05_ACPI_Software_Programming_Model/ACPI_Software_Programming_Model.html#processor-properties-topology-table-pptt

Device Tree

Device Tree 是一种描述硬件的数据结构。内核的启动程序会将设备树加载入内存中,然后通过解析 Device Tree 来获取硬件细节。Device Tree 是树形结构,由一系列被命名的节点和属性组成,节点可以包含子节点,它们之间的关系构成一棵树。属性就是 name 和 value 的键值对。

一个典型的设备树如下图:

5cf1312a-261e-11ed-ba43-dac502259ad0.png

ARM 的 CPU 拓扑是定义在 cpu-map 节点内,cpu-map 是 cpu 节点的子节点。在 cpu-map 节点里可以包含三种子节点:cluster 节点,core 节点,thread 节点。整个 dts 的例子如下:

cpus{
#size-cells=<0>;
#address-cells=<2>;

cpu-map{
cluster0{
cluster0{
core0{
thread0{
cpu=<&CPU0>;
};
thread1{
cpu=<&CPU1>;
};
};

core1{
thread0{
cpu=<&CPU2>;
};
thread1{
cpu=<&CPU3>;
};
};
};

cluster1{
core0{
thread0{
cpu=<&CPU4>;
};
thread1{
cpu=<&CPU5>;
};
};

core1{
thread0{
cpu=<&CPU6>;
};
thread1{
cpu=<&CPU7>;
};
};
};
};
};

//...
};

参考:https://www.kernel.org/doc/Documentation/devicetree/bindings/arm/topology.txt

图来源:https://www.devicetree.org/specifications/

StratoVirt 具体实现

CPUID

首先我们需要计算每个拓扑结构唯一的 topology ID,然后获取或者自己建立相对应的 CPUID entry,当 entry 的 function 的值等于 0xB 和 0X1F 的时候,我们需要根据 CPUID 的规范去设置相对应的 EAX, EBX, ECX 的值。EAX 设置为拓扑 ID,EBX 用来表示那个层级的有几个逻辑处理器,ECX 表示层级号。0xB 需要配置 index 等于 0,1 对应的值,0x1F 需要配置 index 等于 0,1,2 对应的值。下面是相对应的代码:

//cpu/src/x86_64/mod.rs
constECX_INVALID:u32=0u32<< 8;
const ECX_THREAD: u32 = 1u32 << 8;
const ECX_CORE: u32 = 2u32 << 8;
const ECX_DIE: u32 = 5u32 << 8;

impl X86CPUState {
    fn setup_cpuid(&self, vcpu_fd: &Arc)->Result<()>{
//计算topologyID
letcore_offset=32u32-(self.nr_threads-1).leading_zeros();
letdie_offset=(32u32-(self.nr_cores-1).leading_zeros())+core_offset;
letpkg_offset=(32u32-(self.nr_dies-1).leading_zeros())+die_offset;

//获取KVM的fd和获取它支持的CPUIDentries

forentryinentries.iter_mut(){
matchentry.function{
//...
0xb=>{
//ExtendedTopologyEnumerationLeaf
entry.edx=self.apic_idasu32;
entry.ecx=entry.index&0xff;
matchentry.index{
0=>{
entry.eax=core_offset;
entry.ebx=self.nr_threads;
entry.ecx|=ECX_THREAD;
}
1=>{
entry.eax=pkg_offset;
entry.ebx=self.nr_threads*self.nr_cores;
entry.ecx|=ECX_CORE;
}
_=>{
entry.eax=0;
entry.ebx=0;
entry.ecx|=ECX_INVALID;
}
}
}
//0x1f扩展,支持die层级
0x1f=>{
ifself.nr_dies< 2 {
                        entry.eax = 0;
                        entry.ebx = 0;
                        entry.ecx = 0;
                        entry.edx = 0;
                        continue;
                    }

                    entry.edx = self.apic_id as u32;
                    entry.ecx = entry.index & 0xff;

                    match entry.index {
                        0 =>{
entry.eax=core_offset;
entry.ebx=self.nr_threads;
entry.ecx|=ECX_THREAD;
}
1=>{
entry.eax=die_offset;
entry.ebx=self.nr_cores*self.nr_threads;
entry.ecx|=ECX_CORE;
}
2=>{
entry.eax=pkg_offset;
entry.ebx=self.nr_dies*self.nr_cores*self.nr_threads;
entry.ecx|=ECX_DIE;
}
_=>{
entry.eax=0;
entry.ebx=0;
entry.ecx|=ECX_INVALID;
}
}
}
//...
}
}
}

PPTT

根据 ACPI PPTT 表的标准来构建,我们需要计算每个节点的偏移值用于其子节点指向它。我们还需要计算每个节点的 uid,uid 初始化为 0,每增加一个节点 uid 的值加一。还需要根据 PPTT 表的标准计算 Flags 的值。最后需要计算整张表的大小然后修改原来的长度的值。

//machine/src/standard_vm/aarch64/mod.rs
implAcpiBuilderforStdMachine{
fnbuild_pptt_table(
&self,
acpi_data:&Arc>>,
loader:&mutTableLoader,
)->super::Result{
//...
//配置PPTT表头

//添加socket节点
forsocketin0..self.cpu_topo.sockets{
//计算到起始地址的偏移量
letsocket_offset=pptt.table_len()-pptt_start;
letsocket_hierarchy_node=ProcessorHierarchyNode::new(0,0x2,0,socketasu32);
//...
forclusterin0..self.cpu_topo.clusters{
letcluster_offset=pptt.table_len()-pptt_start;
letcluster_hierarchy_node=
ProcessorHierarchyNode::new(0,0x0,socket_offsetasu32,clusterasu32);
//...
forcorein0..self.cpu_topo.cores{
letcore_offset=pptt.table_len()-pptt_start;
//判断是否需要添加thread节点
ifself.cpu_topo.threads>1{
letcore_hierarchy_node=
ProcessorHierarchyNode::new(0,0x0,cluster_offsetasu32,coreasu32);
//...
for_threadin0..self.cpu_topo.threads{
letthread_hierarchy_node=
ProcessorHierarchyNode::new(0,0xE,core_offsetasu32,uidasu32);
//...
uid+=1;
}
}else{
letthread_hierarchy_node=
ProcessorHierarchyNode::new(0,0xA,cluster_offsetasu32,uidasu32);
//...
uid+=1;
}
}
}
}
//将PPTT表添加到loader中
}
}

Device Tree

StratoVirt 的 microvm 使用 device tree 启动,所以我们需要配置 device tree 中的 cpus 节点下的 cpu-map 来使 microvm 支持解析 CPU 拓扑。在 StratoVirt 中,我们支持两层 cluster。我们使用了多层循环来创建这个 tree,第一层是创建第一层 cluster,第二层对应创建第二层的 cluster,第三层创建 core,第四层创建 thread。

implCompileFDTHelperforLightMachine{
fngenerate_cpu_nodes(&self,fdt:&mutFdtBuilder)->util::Result<()>{
//创建cpus节点
//...

//GenerateCPUtopology
//创建cpu-map节点
letcpu_map_node_dep=fdt.begin_node("cpu-map")?;
//创建第一层cluster节点
forsocketin0..self.cpu_topo.sockets{
letsock_name=format!("cluster{}",socket);
letsock_node_dep=fdt.begin_node(&sock_name)?;
//创建第二层cluster节点
forclusterin0..self.cpu_topo.clusters{
letclster=format!("cluster{}",cluster);
letcluster_node_dep=fdt.begin_node(&clster)?;
//创建core节点
forcorein0..self.cpu_topo.cores{
letcore_name=format!("core{}",core);
letcore_node_dep=fdt.begin_node(&core_name)?;
//创建thread节点
forthreadin0..self.cpu_topo.threads{
letthread_name=format!("thread{}",thread);
letthread_node_dep=fdt.begin_node(&thread_name)?;
//计算cpu的id
//letvcpuid=...
//然后添加到节点中
}
fdt.end_node(core_node_dep)?;
}
fdt.end_node(cluster_node_dep)?;
}
fdt.end_node(sock_node_dep)?;
}
fdt.end_node(cpu_map_node_dep)?;

Ok(())
}
}

这个代码构建出来设备树的结构和前面原理中展示的结构基本一致

验证方法

我们可以通过下面的命令启动一个虚拟机,smp 参数用来配置 vCPU 拓扑

sudo./target/release/stratovirt
-machinevirt
-kernel/home/hwy/std-vmlinux.bin.1
-appendconsole=ttyAMA0root=/dev/vdarwreboot=kpanic=1
-drivefile=/usr/share/edk2/aarch64/QEMU_EFI-pflash.raw,if=pflash,unit=0,readonly=true
-drivefile=/home/hwy/openEuler-22.03-LTS-stratovirt-aarch64.img,id=rootfs,readonly=false
-devicevirtio-blk-pci,drive=rootfs,bus=pcie.0,addr=0x1c.0x0,id=rootfs
-qmpunix:/var/tmp/hwy.socket,server,nowait
-serialstdio
-m2048
-smp4,sockets=2,clusters=1,cores=2,threads=1

接着,我们可以通过观察 /sys/devices/system/cpu/cpu0/topology 下面的文件来查看配置的 topology。

[root@StratoVirttopology]ll
total0
-r--r--r--1rootroot64KJul1809:04cluster_cpus
-r--r--r--1rootroot64KJul1809:04cluster_cpus_list
-r--r--r--1rootroot64KJul1809:04cluster_id
-r--r--r--1rootroot64KJul1809:04core_cpus
-r--r--r--1rootroot64KJul1809:04core_cpus_list
-r--r--r--1rootroot64KJul1809:01core_id
-r--r--r--1rootroot64KJul1809:01core_siblings
-r--r--r--1rootroot64KJul1809:04core_siblings_list
-r--r--r--1rootroot64KJul1809:04die_cpus
-r--r--r--1rootroot64KJul1809:04die_cpus_list
-r--r--r--1rootroot64KJul1809:04die_id
-r--r--r--1rootroot64KJul1809:04package_cpus
-r--r--r--1rootroot64KJul1809:04package_cpus_list
-r--r--r--1rootroot64KJul1809:01physical_package_id
-r--r--r--1rootroot64KJul1809:01thread_siblings
-r--r--r--1rootroot64KJul1809:04thread_siblings_list

比如:

catcore_cpus_list

结果是

0

表示和 cpu0 同一个 core 的 cpu 只有 cpu0。

catpackage_cpus_list

会显示

0-1

表示和 cpu0 同一个 socket 的 cpu 有从 cpu0 到 cpu1。

下面这些工具也可以辅助进行验证。

比如:lscpu

lscpu

通过执行 lscpu 命令会出现下面结果

Architecture:aarch64
CPUop-mode(s):32-bit,64-bit
ByteOrder:LittleEndian
CPU(s):64
On-lineCPU(s)list:0-63
VendorID:ARM
Modelname:Cortex-A72
Model:2
Thread(s)percore:1
Core(s)percluster:16
Socket(s):-
Cluster(s):4
Stepping:r0p2
BogoMIPS:100.00
Flags:fpasimdevtstrmaespmullsha1sha2crc32cpuid
NUMA:
NUMAnode(s):4
NUMAnode0CPU(s):0-15
NUMAnode1CPU(s):16-31
NUMAnode2CPU(s):32-47
NUMAnode3CPU(s):48-63
Vulnerabilities:
Itlbmultihit:Notaffected
L1tf:Notaffected
Mds:Notaffected
Meltdown:Notaffected
Specstorebypass:Vulnerable
Spectrev1:Mitigation;__userpointersanitization
Spectrev2:Vulnerable
Srbds:Notaffected
Tsxasyncabort:Notaffected

审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 处理器
    +关注

    关注

    68

    文章

    18288

    浏览量

    222167
  • 英特尔
    +关注

    关注

    60

    文章

    9425

    浏览量

    168834
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10446

    浏览量

    206567
  • SMP
    SMP
    +关注

    关注

    0

    文章

    68

    浏览量

    19450

原文标题:StratoVirt 的 vCPU 拓扑(SMP)

文章出处:【微信号:openEulercommunity,微信公众号:openEuler】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    AliOS Things SMP系统及其在esp32上实现示例

    快速实现AliOS Things在各种不同多核CPU架构下的移植。AliOS Things实现了基本的SMP调度框架,支持多CPU体系的系统运行和调度机制。多
    发表于 05-15 12:45

    SMP04|SMP22|信号源|SMP04 现金回收

    SMP04|SMP22|信号源|SMP04 现金回收欧阳R:***QQ:734645067回收工厂或个人、库存闲置、二手仪器及附件。长期 销售、维修、回收 高频 二手仪器。温馨提示:如果您
    发表于 12-31 17:51

    PCIE基本概念与拓扑架构

    1 PCIE基本概念1.1 PCIE拓扑架构图1.2 PCIE Switch内部结构图1.3 PCIE协议结构图2 PCIE枚举原理2.1 Type0&Type1配置头空间2.2 拓扑示例
    发表于 02-16 06:08

    StratoVirt 的 vCPU 拓扑SMP

    CPU 拓扑用来表示 CPU 在硬件层面的组合方式,本文主要讲解 CPU 拓扑
    发表于 08-30 13:57

    RT-Thread SMP和AMP初体验简介

    。首先在进入到 bsp/qemu-vexpress-a9 目录,进入 menuconfig ,使能 SMP ,并且将 CPU 的个数设置为实际值(4个);可以在次 CPU 的线程添加
    发表于 02-03 14:33

    RT-Thread框架下的SMP支持

    _idle_exec() 这三个函数即可,具体的移植介绍可以参考 RT-Thread 文档中心SMP 介绍与移植关于 CPU 还未支持 SMP 的平台RT-Thread 还有一些
    发表于 02-13 15:02

    如何启用SMP

    我知道如何启用SMP。开启SMP后, [color=\\\"#FF0000\\\"]在用户空间使用SMP应该怎么做,比如怎么写代码,怎么指定CPU核心,怎么开启负载均衡
    发表于 05-16 06:00

    SMP-04采样保持四放大器和SMP-08 SMP-18采样

    SMP-04采样保持四放大器和SMP-08 SMP-18采样保持八放大器的应用:
    发表于 06-03 14:54 34次下载
    <b class='flag-5'>SMP</b>-04采样保持四放大器和<b class='flag-5'>SMP</b>-08 <b class='flag-5'>SMP</b>-18采样

    SMP技术

    SMP技术 SMP英文全称为Symmetrical Multi-Processing,意指“对称多处理”技术,是指在一个计算机上汇集了一组处理器—即多CPU,各CPU
    发表于 12-17 14:08 4404次阅读

    Linux在SMP系统上的移植研究

    基于自主开发以双核嵌入式CPU EM8301为处理核心的嵌入式应用的目的,针对双核CPU芯片的系统结构和Linux内核的特性,通过研究嵌入式Linux操作系统在SMP系统上的移植,探讨SMP
    发表于 11-14 16:09 11次下载
    Linux在<b class='flag-5'>SMP</b>系统上的移植研究

    华纳云浅析海外服务器的3种体系架构SMP、NUMA、MPP

    华纳云浅析海外服务器的3种体系架构SMP、NUMA、MPP
    的头像 发表于 12-14 11:09 776次阅读

    SMP是什么?多核芯片(SMP)的启动方法

    SMP 英文为Symmetric Multi-Processing ,是对称多处理结构的简称,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统以及总线结构,一个服务器系统可以同时运行多个处理器,并共享内
    的头像 发表于 07-26 09:26 8880次阅读
    <b class='flag-5'>SMP</b>是什么?多核芯片(<b class='flag-5'>SMP</b>)的启动方法

    什么是走线的拓扑架构?怎样调整走线的拓扑架构来提高信号的完整性?

    什么是走线的拓扑架构?怎样调整走线的拓扑架构来提高信号的完整性? 走线的拓扑架构是指电子设备内部
    的头像 发表于 11-24 14:44 321次阅读

    SMP是什么 启动方式介绍

    SMP是什么? SMP 英文为Symmetric Multi-Processing ,是对称多处理结构的简称,是指在一个计算机上汇集了一组处理器(多CPU),各CPU之间共享内存子系统
    的头像 发表于 12-05 15:23 993次阅读

    SMP多核启动cpu操作函数

    _ops回调 其中spin-table启动方式的回调如下: const struct cpu_operations smp_spin_table_ops = {.name= "spin-table
    的头像 发表于 12-05 16:04 288次阅读
    <b class='flag-5'>SMP</b>多核启动<b class='flag-5'>cpu</b>操作函数