PCIe配置
更新时间:2024/12/19
在Gitcode上查看源码

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编写规范,如下所示:

json
{
    "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的配置类似):

json
"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卡。在上述的配置中,对BomIdAuxId进行了配置,并把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 Connector
  • UBCDD: 一种连接器,用于传输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描述了BCUIEU之间的连接关系。在PSR中描述了多个UnitConfiguration的配置,其中配置类产品的线缆配置白名单,例如:

json
"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的定义,如下所示:

json
"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中定义了该线路的属性:

json
"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的定义。

json
"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对应,从而完成映射关系的连续。

自此,我们的BCUIEU端口之间的联系已构建完成(即Topo关系)。接下来就是pcie_device组件仓中对RootBDF的计算。

计算RootBDF

在pcie_device中会利用portIdSocketID完成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

对于以上的案例,可知socketIdportId分别为114,随后可以在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),并用四元组拼接成AuxIdID,以完成sr文件名的拼接,即Bom_{DeviceID+VendorID}_{SubDeviceID+SubVendorID}.sr。最后完成sr的加载

天池PCIe卡的加载

与非天池PCIe卡不一样的地方在于,天池PCIe卡的SR加载来自于Eeprom。例如:

json
"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的拓扑建立中介绍了BCUIEU之间的端口映射关系,因此对于新增PCIe卡的CSR配置,您必须在执行前确定如下配置是否正确:

  • PSR仓中是否添加UnitConfiguration_IEU的配置,其UID和SrcPortName等配置项是否正确配置?
  • BCU的CSR中是否正确配置SerDes与Riser卡的端口映射
  • Riser卡的Connector"BusinessConnector是否正确配置

在完成如上配置之后,才能够开始PCIe卡的CSR配置。对于PCIe卡对象而言,必须配置PCIeDevicePCIeCard对象。其余对象需要根据PCIe卡的能力进行额外配置。例如对于PCIe网卡,需要额外配置NetworkAdapterNetworkPort等等,对于GPU卡,需要配置GPU对象。在此,我们简要介绍PCIe卡的基础CSR配置:

json
{
    "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的相关信息。

shell
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