openUBMC在自发现流程中采用了动态加载PCIe的模式加载各种类型的PCIe卡。
为了更好的学习本章知识,请先行完成如下课程:
认识Riser、PCIe卡
PCIe全称 PCI Express,Peripheral Component Interconnect Express,是一种计算机总线标准。PCIe相较于传统的PCI总线,具有更高的带宽和更高的速度,能够提供更快的数据传输速度和更稳定的性能。同时PCIe还支持热插拔和多路复用,可以同时连接多个设备,提高了系统的扩展性和灵活性。PCIe的版本不断更新,目前最新的版本为PCIe 5.0,可以提供更高的带宽和更快的速度。
PCIe卡是一种计算机扩展卡,它使用PCI Express总线接口连接到计算机主板,可以提供额外的功能和性能。PCIe卡可以用于增加计算机的存储容量、网络功能、图形处理能力等。常见的PCIe卡有显卡、网卡、声卡、存储卡、RAID卡等等。
在服务器中,通常将PCIe卡插在Riser卡的PCIe卡槽中,其中Riser卡是一种用于扩展计算机主板插槽的适配器卡。它通常用于将主板上的插槽方向从垂直变为水平,以便更容易地容纳其他内部组件,尤其是在小型或限制空间的计算机机箱中。
PCIe设备加载流程
在自发现流程中我们介绍了硬件加载流程,Connector
作为硬件之间的连接器,保证了openUBMC能够通过Connector
一级一级地进行对象的加载和分发。关于Connector
的详细描述请参考《板卡适配》。
Riser卡的加载
在服务器中,PCIe卡通常是插在Riser卡上的,因此在加载PCIe卡之前需要确保Riser卡的正常加载。为此我们需要定义Riser卡的CSR编写规范,如下所示:
{
"FormatVersion": "x.xx",
"DataVersion": "x.xx",
"ManagementTopology": {
"Anchor": {
"Buses": [
"Hisport_x"
]
},
"Hisport_5": {
"Chips": [
"Eeprom_IEU",
"Pca9545_IEU"
]
},
"Pca9545_IEU": {
"Buses": [
"I2cMux_9545Chan2",
"I2cMux_9545Chan3",
"I2cMux_9545Chan4"
]
},
"I2cMux_9545Chan2": {
"Connectors": [
"Connector_PCIe_1",
]
},
"I2cMux_9545Chan3": {
"Connectors": [
"Connector_PCIe_2",
"Connector_PCIe_3",
]
},
"I2cMux_9545Chan4": {
"Chips": [
"Pca9555_IEU",
"Chip_MCU"
]
}
},
"Objects": {
...
}
}
如上所示,在Riser卡的CSR的定义中,ManagementTopology
的定义是CSR语法的必填项。从Topo关系可以看到,此张Riser卡上采用了Mux器件对链路进行了复用,并且支持加载多张PCIe卡。在Objects
中需要同步的定义Connector
,此处以Connector_PCIe_1
为例(其余Connector的配置类似):
"Connector_PCIe_1": {
"Bom": "14140130", // 下级器件对应的BomID
"Id": "xxxx", // 下级器件对应的id
"AuxId": "xxxx", // 下级器件对应的AuxId
"Slot": 2, // 下级器件对应的卡槽号
"Position": 2, // 下级器件对应的Position
"Presence": 1, // 在位信息
"Buses": [
"I2cMux_9545Chan2"
],
"SystemId": 1,
"SilkText": "RiserCard${Slot}",
"IdentifyMode": 2, // 加载方式:非天池加载模式
"Container": "Component_RiserCard",
"Type": "PCIe" // 下级器件类型
},
通过配置该PCIe设备的Connector,能够实现在Riser卡上加载PCIe卡,对于特殊的板载网卡也可以在BCU中通过如上的Connector配置PCIe卡。在上述的配置中,对Bom
、Id
、AuxId
进行了配置,并把Presence
置为了1
,这样在加载PCIe卡时就能够加载Bom_Id_AuxId.sr
对应的PCIe设备。
PCIe动态加载
在服务器中,Riser卡能够对接多种PCIe设备,并且对于不同功能的服务器,需要在同一机型上加载不同种类的Riser卡,为此openUBMC提供了一种支持自动获取BDF信息加载不同PCIe设备的加载方式。
通过在Riser卡上配置各个PCIe槽位的CPU管理映射关系,借助带内管理PCIe设备的能力获取PCIe设备的设备BDF信息,以此信息作为PCIe sr文件的索引,实现动态加载不同类型的PCIe设备。
如下图所示,BCU上的CPU通过串行/解串器(SerDes)与UBCDD连接,UBCDD与Riser接口连接,通过在openUBMC中定义这三者之间的关系,就能够实现对PCIe位置的管理。
PCIeSlot1/PCIeSlot2
: 对应IEU上的两个PCIe槽位,即在Riser中对应了两个PCIe ConnectorUBCDD
: 一种连接器,用于传输PCIe高速信号。SerDes
: 串行/解串器,用于将并行数据转换为串行数据进行发送,再将接收到的串行数据转换回并行数据。
CPU资源归属
为了实现CPU对PCIe的资源管理,在配置硬件SR时,openUBMC提出了如下的概念:
UnitConfiguration
: 其中配置了Riser卡、UBCDD和SerDes之间的关系。该配置在PSR中配置,以Riser的UID作为Key。同一个PSR中能够配置多个UnitConfiguration用于支持多张Riser配置。BusinessConnector
:业务Connector,用于定义Riser对应PCIeSlot的上下行带宽和绑定的PCIeConnector,同时其中定义了UID信息,用于定位PSR中对应的UnitConfiguration。PCIeAddrInfo
:PCIe对应的UnitConfiguration配置,与BusinessConnector绑定。SerDes
:在BCU的sr中配置串行/解串器的硬件信息,包含每条SerDes的对应的RootBDF的Device号。
建立CPU对PCIe的资源管理过程,称为PCIe Topo建立。
PCIe Topo建立 与 RootBDF计算
PCIe设备中BDF(Bus, Device, Function)表示设备在PCIe总线上的位置,其中Bus表示设备所连接的PCIe总线编号,Device表示设备在该PCIe总线上的设备号,Function表示设备上具体的功能号。
BDF地址是由三个数字组成的,比如“05:02.1”,表示总线号是5,设备号是2,函数号是1。BDF地址用于唯一标识PCIe设备,并且是PCIe设备通信和访问的依据。
在一个PCIe拓扑结构中,每个PCIe设备都有其独特的BDF位置,而RootBDF位置则是由所连接的PCIe根总线决定的。因此,BDF和RootBDF是不同的两个概念,也就是说,同一个PCIe设备的RootBDF在不同的PCIe拓扑结构下可能不同,但其BDF位置不变。
BCU端口和IEU端口映射关系
在获取PCIe设备的BDF之前需要确定PCIe槽位与CPU资源的对应关系,以及计算其RootBDF值。为了计算某个PCIe槽位在哪个SerDes上,即RootBDF是多少,我们采用了UnitConfiguration
描述了BCU
和IEU
之间的连接关系。在PSR
中描述了多个UnitConfiguration
的配置,其中配置类产品的线缆配置白名单,例如:
"UnitConfiguration_IEU3": {
"SlotType": "IEU",
"SlotNumber": 3,
"SlotSilkText": "IEUSlot3",
"Configurations": [
{
"UID": "xxxx",
"Index": 0,
"BCUIndex": 1,
"SrcPortName": ["B4c", "B4a"],
"TargetPortID": [ 17, 49],
"Slot": [7, 8],
"Device": [8 , 12],
}
],
"Port1LinkInfo": ""
}
- UID: IEU卡的UID,用于匹配对应的PCIeAddrInfo
- Slot:全局唯一,硬件提供
- SrcPortName:BCU端UBCDD的B4c端口对应BCU的槽位7,B4a端口对应BCU的槽位8
- TargetPortID:Riser向上连接的端口号(10进制)
BCU配置SerDes
在BCU
的sr中同理要配置对应SerDes
的定义,如下所示:
"BusinessConnector_CPU2UBCDD2": {
"Direction": "Downstream",
"BCUIndex": "${Slot}",
"Slot": 8,
"LinkWidth": "X16", // 带宽为x16
"MaxLinkRate": "PCIe 4.0",
"ConnectorType": "UBCDD",
"SilkText": "CPU2 UBCDD2",
"UpstreamResources": [
{"Name": "SerDes_1_8","ID": 8,"Offset": 0,"Width": 8},
{"Name": "SerDes_1_7","ID": 7,"Offset": 0,"Width": 4},
{"Name": "SerDes_1_10","ID": 10,"Offset": 0,"Width": 4}
],
"ActualResourceOrder": ["SerDes_1_10","SerDes_1_7","SerDes_1_8"],
"Ports": [
{"Name": "B4a","ID": 13,"Offset": 0,"Width": 8},
{"Name": "B4c","ID": 15,"Offset": 8,"Width": 8}
],
"Port1LinkInfo": "",
"Port2LinkInfo": ""
},
- LinkWidth: 对应PCIe卡槽连接的带宽
- UpstreamResources: 一个PCIe卡槽会对应多个SerDes,上行配置多个SerDes对应的带宽
- ActualResourceOrder和Ports共同决定了实际连接中SerDes与UBCDD上端口的对应关系。上述配置中X8带宽的SerDes_1_8对应B4c(B4d),X4带宽的SerDes_1_7和SerDes_1_10对应B4a(B4b)
以SerDes_1_10
为例,其在BCU
中定义了该线路的属性:
"SerDes_1_10": {
"Name": "SerDes_1_10",
"ID": 10,
"SocketID": 1,
"LinkWidth": 4,
"WorkMode": 1,
"ModeConfigs": [
{
"Mode": 1,
"Device": [14,14,15,15],
"ControllerIndex": [1,1,1,1]
}
]
}
- Device:代表SerDes对应的设备Device为14/14/15/15,这个Device就是之后索引RootBDF时用到的portId
- Socket:代表SerDes连着哪个CPU
IEU配置SerDes
在IEU
侧的sr中同样需要定义与BCU
相连的端口配置。此处配置是对BusinessConnector
的定义。
"BusinessConnector_1": {
"Name": "Up_1",
"Direction": "Upstream",
"Slot": 1,
"LinkWidth": "X16",
"MaxLinkRate": "PCIe 4.0",
"ConnectorType": "UBCDD",
"Ports": [
{"Name": "Down_1","ID": 49,"Offset": 0,"Width": 8},
{"Name": "Down_2","ID": 17,"Offset": 8,"Width": 8}
]
},
"BusinessConnector_2": {
"Name": "Down_1",
"Direction": "Downstream",
"Slot": 1,
"LinkWidth": "X8",
"MaxLinkRate": "PCIe 4.0",
"ConnectorType": "PCIe CEM",
"UpstreamResources": [
{"Name": "Up_1","ID": 1,"Offset": 0,"Width": 8 }
],
"RefMgmtConnector": "#/Connector_PCIE_1",
"RefPCIeAddrInfo": "#/PcieAddrInfo_1"
},
"PcieAddrInfo_1": {
"Location": "RiserCard${Slot}",
"ComponentType": 8,
"ContainerSlot": "${Slot}",
"ContainerUID": "xxx", // 与UnitConfiguration_IEU3中的UID对应
"ContainerUnitType": "IEU",
"GroupPosition": "PcieAddrInfo_1_${GroupPosition}"
},
在BusinessConnector_1中配置Ports,定义其下行路线,其中定义的ID
能够与对应的UnitConfiguration中的TargetPortID
对应,从而对应"B4a"。在PcieAddrInfo
中通过配置ContainerUnitType
与UnitConfiguration中的UID对应,从而完成映射关系的连续。
自此,我们的BCU
和IEU
端口之间的联系已构建完成(即Topo关系)。接下来就是pcie_device组件仓中对RootBDF
的计算。
计算RootBDF
在pcie_device中会利用portId
和SocketID
完成RootBDF
的计算。
1. pcie_biz_connectors_init函数:根据IEU/SEU上行连接器(Upstream+Ports)的第一个ID,即construct_biz_topo函数中的first_target_port_id
2. match_src_connector函数:通过first_target_port_id找到对应unitconfiguration中的SrcPortName,通过SrcPortName得到BCU上的src_connector(is_src_biz_connector函数判断)
3. 通过src_connector和偏移得到Serdes对象中的SocketId和Device,Device即为portId,SocketId为cpuid
4. 得到portId后通过override_root_bdf+socketId+portId可以计算得到RootBDF
对于以上的案例,可知socketId
和portId
分别为1
和14
,随后可以在root_bdf表中查询对应的RootBDF
。最终找到对应的RootBDF
为[0xAA, 0x2, 0]
。
PCIe卡的加载
由于PCIe卡在业务场景下会出现热插拔的情况,为此设计了基于BDF的方案,即BIOS启动阶段主动上报每个PCIe槽位的BDF信息。BMC再通过BDF信息去PMU查询对应BDF的四元组信息,再加载对应的PCIe设备CSR。如下图所示:
PCIe Device组件在初始化的时候会向Bios发送ipmi命令,通过带内拿到PCIe卡的四元组(DeviceID
, VendorID
, SubDeviceID
, SubVendorID
),并用四元组拼接成AuxId
和ID
,以完成sr
文件名的拼接,即Bom_{DeviceID+VendorID}_{SubDeviceID+SubVendorID}.sr
。最后完成sr
的加载
天池PCIe卡的加载
与非天池PCIe卡不一样的地方在于,天池PCIe卡的SR加载来自于Eeprom。例如:
"Connector_PCIE_SLOT2TianChi": {
"Id": "", // 下级器件对应的id
"AuxId": "", // 下级器件对应的AuxId
"Bom": "14140130", // 下级器件对应的BomID
"Slot": 2, // 下级器件对应的卡槽号
"Position": 5, // 下级器件对应的Position
"Presence": "<=/Scanner_Slot2Presence.Value|>expr($1 == 1? 0 : 1)", // 天池组件的在位信号由Scanner获取
"Buses": [
"I2cMux_Pca9545_PCA9545_2"
],
"SystemId": 1,
"SilkText": "RiserCard${Slot}",
"IdentifyMode": 3, // 加载方式: 天池加载模式
"Container": "Component_RiserCard",
"Type": "PCIe", // 下级器件类型
"IdChipAddr": 160
}
其余配置与非天池PCIe设备相同。
PCIe CSR配置
在PCIe的拓扑建立中介绍了BCU
和IEU
之间的端口映射关系,因此对于新增PCIe卡的CSR配置,您必须在执行前确定如下配置是否正确:
- PSR仓中是否添加UnitConfiguration_IEU的配置,其UID和SrcPortName等配置项是否正确配置?
BCU
的CSR中是否正确配置SerDes与Riser卡的端口映射- Riser卡的
Connector
和"BusinessConnector
是否正确配置
在完成如上配置之后,才能够开始PCIe卡的CSR配置。对于PCIe卡对象而言,必须配置PCIeDevice
和PCIeCard
对象。其余对象需要根据PCIe卡的能力进行额外配置。例如对于PCIe网卡,需要额外配置NetworkAdapter
、NetworkPort
等等,对于GPU卡,需要配置GPU
对象。在此,我们简要介绍PCIe卡的基础CSR配置:
{
"FormatVersion": "x.xx",
"DataVersion": "x.xx",
"Unit": {
"Type": "PCIeCard",
"Name": "PCIeCard_1"
},
"ManagementTopology": {
...
},
"Objects": {
"PCIeDevice_1": {
"DeviceName": "PCIe Card $ (name)", // PCIe卡的名称
"FunctionClass": x, //功能分类(0:未知、1:RAID、2:网卡、3:GPU卡...)
"Position": "", // 下级器件对应的Position
"DiagnosticFault": 0, // 故障标志位
"PredictiveFault": 0, // 预故障标志位
"BandwidthReduction": 0, // 降带宽属性
"LinkSpeedReduced": 0, // PCIe设备降速率事件属性
"CorrectableError": 0, // PCIe设备发生可纠正错误,取值:0-未发生可纠正错误,1-发生可纠正错误
"UncorrectableError": 0, // PCIe设备发生不可纠正错误,取值:0-未发生不可纠正错误,1-发生不可纠正错误
"FatalError": 0, // PCIe设备发生致命错误,取值:0-未发生致命错误,1-发生致命错误
"Container": "${Container}",
"GroupPosition": "PCIeDevice_${GroupPosition}"
},
"PCIeCard_1": {
"SlotID": "<=/PCIeDevice_1.SlotID",
"NodeID": "<=/PCIeDevice_1.SlotID |> string.format('PCIeCard%s',$1)",
"Name": "xxxx",
"BoardName": "xxx",
"BoardID": 255,
"Description": "xxxx",
"FunctionClass": x, // 功能分类(0:未知、1:RAID、2:网卡、3:GPU卡...)
"VendorID": 0000, // 四位十进制(四元组之一,制造商ID)
"DeviceID": 0000, // 四位十进制(四元组之一,设备ID)
"SubVendorID": 0000, // 四位十进制(四元组之一,子制造商ID)
"SubDeviceID": 0000, // 四位十进制(四元组之一,子设备ID)
"Position": "<=/PCIeDevice_1.Position", // 下级器件对应的Position
"LaneOwner": "<=/PCIeDevice_1.SocketID", // 资源归属,起始值为1,表示当前卡挂在哪个CPU下
"FirmwareVersion": "N/A",
"Manufacturer": "xxx",
"PartNumber": "xxxxx", // 部件号
"RefChip": "#/Chip_xxx",
"Protocol": "xxxx", // 使用协议
"MaxFrameLen": 64,
"Model": "xxxx",
"DeviceName": "<=/PCIeDevice_1.DeviceName",
"LinkSpeed": "", // 链路速度
"LinkSpeedCapability": "", // 最大链路速度
"PcbID": "#/Accessor_PcbID.Value", // Pcb
"PcbVersion": ".x", // Pcb版本
"Health": "", // 健康状态。0:正常;1:一般;2:严重;3:紧急。
"DevBus": "<=/PCIeDevice_1.DevBus", // 设备BDF,BUS
"DevDevice": "<=/PCIeDevice_1.DevDevice", // 设备BDF,Device
"DevFunction": "<=/PCIeDevice_1.DevFunction", // 设备BDF,Fucntion
"SerialNumber": "" // 序列号
},
... // PCIe卡特性Object编写
}
}
带内查询PCIe
通过上述的介绍,我们可知PCIe设备的BDF是由Bios进行分发的,因此我们也可以通过带内os查询PCIe的相关信息。
dmidecode --type 9
如上的命令将会从SMBIOS中获取PCIe信息,其中包含PCIe设备的槽位、带宽、使用情况等等。
另外也可以通过lspci -tv
查看带内PICe设备的加载情况。
线缆检测
线缆检测告警呈现
PCIe的线缆检测主要检测UBC端口和IEU端口的联系是否符合在PSR中的线缆连接配置。
线缆告警:
- 0x28000033(Cable.UBNotPresent)代表对于某个组件的线缆配置,当前线缆连线缺了
- 0x28000031(Cable.UBIncorrectConnection)代表对于某个IEU,A1口预期连BCU的B1,A2口预期连BCU的B2,但是IEU A1口连了BCU B2口,连错了。
- 0x28000035(Cable.UnitNotSupported)代表对于某个IEU的口,连了BCU的A口,但是在配置中不需要连A口。
线缆检测流程
pcie_device
提供了循环的线缆检测方案,主要用于比较CSR中的线缆连接配置与BCU实际的线缆连接之间的差异性。其能够完成双向的检测:
- CSR变更时,线缆检测能够检查CSR的配置是否正确。
- CSR无变更时,线缆检测能够检查出当前器件是否存在线缆问题。
具体的实现方式是通过BCU
配置对应的SMC
命令字收集Topo信息,然后再进行比较,例如:
[B4a][0x31,000000104030209999,0]
[B4c][0x11,00000001040302088888,0]
[A3a][0x31,0000000104030207777,1]
[A3c][0x11,0000000104030206666,1]
如果CSR中的配置与如上的Topo信息出现差异,就会产生线缆检测的告警。
More
具体实现请参考组件pcie_device。