拓扑建立
更新时间: 2026/04/08
在Gitcode上查看源码

拓扑建立

拓扑建立的主要目的是生成SlotIdPortId的关系,再将其填写到对应的PcieAddrInfo中。其中PortId对应的是Serdes中配置的Device表中的值,SlotId则根据PSR和IEU/EXU/SEU中相应下行接口共同决定,拓扑建立匹配如下图。

之所以需要拓扑建立,是因为IEU/EXU/SEU等组件有多种连接到BCU上的插法,而PCIe设备槽位的PortId和SlotId是和插法有关的,BMC需要根据实际的插法计算每个槽位的PortId和SlotId。

拓扑建立的结果会被bios组件用以生成丝印,在较新版本的BMC中,丝印文件为一键收集中/dump_info/AppDump/bios/1/silkconfig.json,在旧版本中需要在BMC的文件系统中找,路径为/data/opt/bmc/conf/bios/silkconfig/json

打开丝印文件后,关注以下内容:

json
"PCIeSilk": [{
    "RootPortDeviceId": 4,
    "SocketId": 0,
    "Segment": 0,
    "SlotId": 1,
    "Slik": "",
    "DeviceType": "OCP"
}, {
    "RootPortDeviceId": 20,
    "SocketId": 1,
    "Segment": 0,
    "SlotId": 47,
    "Slik": "",
    "DeviceType": "NVME"
}, {
    "RootPortDeviceId": 8,
    "SocketId": 0,
    "Segment": 0,
    "SlotId": 2,
    "Slik": "",
    "DeviceType": "PCIe"
}]

以上展示了三种设备的丝印,分别是OCP卡,NVME硬盘和PCIe卡,这三种设备的加载依赖带内host的上报。这三种类型的槽位是分开计算的,一般来说,

丝印文件是根据PcieAddrInfo资源树对象生成的,对应关系如下:

PcieAddrInfo中属性丝印文件中的属性说明
PortIDRootPortDeviceId注意二者名称不同
SegmentSegment
SlotIDSlotId注意大小写
SocketIDSocketId注意大小写
ComponentTypeDeviceType二者不是直接对应,ComponentType为整数,DeviceType为字符串,其中83对应"OCP",2对应"NVME",8对应"PCIe",此处只列出常见值,完整定义见BCU配置指南。

1. 硬件提供表格示意

CHIPDIEMacroLaneCore编号CHIP0CHIP1
PortId RootBDF PortId RootBDF
XXX CHIPNAM0X4Core0 Port0 01:00.0 Port20 40:00.0
Port1 01:01.0 Port21 40:01.0
M1X4 Port2 01:02.0 Port22 40:02.0
Port3 01:01.0 Port23 40:03.0

2. 拓扑建立组件介绍

拓扑建立主要涉及四种组件,IEU、EXU、SEU和BCU,其中前三种都提供PcieAddrInfo对象,BCU提供Serdes对象。

IEU是Riser卡,负责将BCU上的UBC、UBCDD资源转换为PCIe槽位。

EXU是扩展版,负责将BCU上的UBC、UBCDD资源转换为BMC卡、FlexIO的接口。

SEU是硬盘背板,负责将BCU上的UBC、UBCDD资源或RAID卡出的miniSAS资源转换为NVME或SATA槽位。

BCU是基础板,负责提供UBC、UBCDD资源。

下文以IEU为例。

3. 关键定义

  1. UnitConfiguration:线缆白名单,配置在PSR里。承接BCU(CPU板)和IEU(Riser卡)之间的连接关系。
  2. Serdes:CPU资源,配置在BCU的sr中。包含丝印文件的关键属性RootPortDeviceId,即PcieAddrInfo对象的PortID属性。
  3. BusinessConnector:
    1. BCU下行连接器,配置在BCU的soft部分的CSR里,老版本配置在UbcConfig.sr文件里。包含Serdes和UBC端口PortName的关系。
    2. IEU上行连接器,配置在IEU。包含线缆端口号PortID。
    3. IEU下行连接器,配置在IEU。包含自发现的连接器、对应的PcieAddrInfo、对应的上行连接器名字。
  4. PcieAddrInfo:PCIe槽位,配置在IEU。包含IEU信息以及IEU的槽位等信息。拓扑建立完成后会更新槽位号SlotID、Segment、SocketID、rootBDF(Bus、Device、Function)。

在同一个组件的连接器中,靠近CPU的为上行连接器,靠近PCIe设备的为下行连接器。

以下按顺序sr文件中的各个关键对象,其中BusinessConnector有三种。

  1. UnitConfiguration 类,配置在PSR中
"UnitConfiguration_IEU1": {
    "SlotType": "IEU",   // 对应PcieAddrInfo 类里 ContainerUnitType 属性
    "SlotNumber": 1,     // 对应PcieAddrInfo 类里 ContainerSlot 属性
    "SlotSilkText": "IEUSlot1",
    "Configurations": [
        {
            "UID": "00000001040302044499",  // 对应PcieAddrInfo 类里 ContainerUID 属性
            "Index": 1,                     // 对应线缆应该和哪个CPU链接,CPLD会上报是哪个CPU的连线,如果配置为0或不配置的话不检测
            "BCUIndex": 1,                  // 对应线缆应该和哪个BCU链接,CPLD通过哪个BCU获取就对应哪个BCU(部分机型在CPLD里会上报一个BCUIndex),如果配置为0或不配置则不检测
            "SrcPortName": [
                "A1a",   // 索引和 TargetPortID 里的索引一致。后续解析成转成 zone、src_port_id。对应BCU下行连接器里的 Ports 的 name 属性
                "A2a"
            ],
            "TargetPortID": [
                49,     // 对应 IEU下行连接器里的 first_res_port_id,即IEU上行连接器里的Ports元素的ID
                33
            ],
            "Slot": [
                2,     // 对应 PcieAddrInfo 里的 SlotID
                3
            ],
            "Device": [
                8,      //  对应 PcieAddrInfo 里的 PortID,一般不配置,部分场景需要在PSR中配死,不去BCU中寻找
                12
            ]
        },
        ...
    ],
    "Port1LinkInfo": ""
},
  1. SerDes 类,配置在BCU中,连接BCU下行连接器
"SerDes_1_8": {
    "Name": "SerDes_1_8",
    "ID": 8,
    "SocketID": 1,       // 对应哪一个CPU,从0开始
    "LinkWidth": 8,
    "WorkMode": 1,       // 工作状态, 1 = PCIe, 4 = SAS, 3 = SATA
    "ModeConfigs": [
        {
            "Mode": 1,   // 匹配对应的工作模式 WorkMode 属性,根据上级的WorkMode匹配ModeConfigs的具体项目
            "Device": [
                8,       // device 值,后续对应 PcieAddrInfo 里的 PortID
                8,
                9,
                9,
                10,
                10,
                11,
                11
            ],
            "ControllerIndex": [
                1,      // 对应Core编号
                1,
                1,
                1,
                1,
                1,
                1,
                1
            ]
        }
    ]

    // 以上是CSR配置,以下是bmc代码里设置的属性
    start_lane        // 匹配到的BCU下行连接器累计的当前 LinkWidth (多个serdes对应一个BCU下行连接器)
}
  1. 1.BCU 下行连接器,对应BCU上的UBC/UBCDD端口:
"BusinessConnector_CPU2UBCDD2": {
    "Direction": "Downstream",
    "Slot": 8,
    "LinkWidth": "X16",
    "MaxLinkRate": "PCIe 4.0",
    "ConnectorType": "UBCDD",
    "SilkText": "CPU2 UBCDD2",
    "UpstreamResources": [
        {
            "Name": "SerDes_1_8", // 用来匹配SerDes的 Name。数组顺序对应SerDes的真实顺序。
            "ID": 8,
            "Offset": 0,
            "Width": 8
        },
        ...
    ],
    "ActualResourceOrder": [
        "SerDes_1_10",   // 对应res_serdeses的真实顺序,如果配了这个字段,就按照这个顺序重排res_serdeses
        "SerDes_1_7",
        "SerDes_1_8"
    ],
    "Ports": [
        {
            "Name": "B4a", // 这里是端口名,主要用来匹配,B是所属的Zone
            "ID": 13,      // 这里的ID是源端的ID,注意和TargetPortID区分
            "Offset": 0,  // 也是对应的IEU下行连接器开始的lane
            "Width": 8
        },
        ...
    ],
    "Port1LinkInfo": "",
    "Port2LinkInfo": ""

    // 以上是CSR配置,以下是bmc代码里设置的属性
    res_serdeses        // 匹配到的所有的 SerDes
},
  1. 2.IEU上行连接器:
"BusinessConnector_3": {
    "Name": "Up_1",
    "Direction": "Upstream",
    "Slot": 1,
    "LinkWidth": "X8",
    "MaxLinkRate": "PCIe4.0",
    "ConnectorType": "UBC",
    "Ports": [
        {
        "Name": "Port1",
        "ID": 49,
        "Offset": 0,          // 这个Offset主要用来索引Port,和BCU中PortID的索引无关
        "Width": 8
        }
    ]
},
  1. 3.IEU下行连接器(pcie卡的连接器):
"BusinessConnector_1": {
    "Name": "Down_1",
    "Direction": "Downstream",
    "Slot": 1,                      // 找 PcieAddrInfo 对象的 SlotID 用到,作为数组下标去PSR的Slot数组里查找。
    "LinkWidth": "X16",
    "MaxLinkRate": "PCIe 4.0",
    "ConnectorType": "PCIe CEM",
    "UpstreamResources": [
        {
            "Name": "Up_1", // 用来匹配上行连接器的 Name
            "ID": 255,
            "Offset": 0,    // 用来匹配上行连接器端口的 Offset
            "Width": 16
        }
    ],
    "RefMgmtConnector": "#/Connector_PCIE_1",  // 关联框架自发现的连接器
    "RefPCIeAddrInfo": "#/PcieAddrInfo_1"    // 关联对应的PcieAddrInfo对象

    // 以上是CSR配置,以下是bmc代码里设置的属性
    res_connectors:        // 匹配到的所有的上行连接器
    res_port_ids:     // 匹配到的上行连接器的端口的ID(可能有多个)
    port_offsets:           // 匹配到的上行连接器的端口的Offset(可能有多个,和res_port_ids中的对象一一对应)
    link_width:            // 解析 LinkWidth 的数字
    ref_pcie_addr_info:     // 根据RefPCIeAddrInfo匹配的pcie_addr_info对象
    src_connector:          // 匹配到的BCU下行连接器。
    ref_mgmt_connector          // 根据load_info信息,主要是type和SlotID,找到并重新获取资源树对象。
    ref_mgmt_connector_tianchi  // 根据load_info信息,主要是type和SlotID,找到并重新获取资源树对象。
},
  1. PcieAddrInfo 类:
"PcieAddrInfo_1": {
    "Location": "RiserCard${Slot}",
    "ComponentType": 8,
    "ContainerSlot": "${Slot}",                 // 对应psr里 UnitConfiguration 对象 SlotNumber 属性
    "ContainerUID": "00000001040302044498",     // 对应psr里 UnitConfiguration 对象 Configurations 属性里元素的 UID 属性。
    "ContainerUnitType": "IEU",                 // 对应psr里 UnitConfiguration 对象 SlotType 属性
    "GroupPosition": "PcieAddrInfo_1_${GroupPosition}"  // 主键不能重复,GroupPosition 保证了不同CSR之间不重复。
    
    // 以上是CSR配置,以下是bmc代码里设置的属性
    SlotID:         // 来自 UnitConfiguration 里的 Configurations 的 Slot 数组里的元素,IEU下行连接器Slot的值作为索引。
    PortID:         // 来自 SerDes 或 UnitConfiguration 类的 device值
    SocketID:      // 来自最高位的 serdes 类的 SocketID 属性
    ControllerIndex: // 来自最高位的 serdes 类的 ControllerIndex 值
    Bus             // 根据 SocketID 和 PortID 在 override_root_bdf 或 BDFConfig 里找到。
    Device          // 根据 SocketID 和 PortID 在 override_root_bdf 或 BDFConfig 里找到。
    Function        // 根据 SocketID 和 PortID 在 override_root_bdf 或 BDFConfig 里找到。
    DevBus          // 从持久化数据读取 或者监听bios上报的devBDF
    DevDevice       // 从持久化数据读取 或者监听bios上报的devBDF
    DevFunction     // 从持久化数据读取 或者监听bios上报的devBDF
},

4. 匹配示意:

因为代码里的流程是从PCIe槽位开始,向上匹配找到Serdes对象,所以下图以PcieAddrInfo对象开始,详细介绍如何关联各个CSR里的对象。

  1. IEU下行连接器的UpstreamResources数组里的第一个元素的NameOffset,找到IEU上行连接器的Ports的元素。比如上面的接线方式最终找到ID=49和33。
  2. 拓扑建立选择对应的PSR中的UnitConfiguration时,会先根据SlotTypeSlotNumber,如某个IEU,对应的PcieAddrInfo的ContainerSlot = 1,对应PSR中UnitConfiguration.SlotType = "IEU"UnitConfiguration.SlotNumber = 1
  3. UnitConfiguration.Configurations只有一项时,默认选择这一项;否则,拓扑建立会根据实际线缆信息匹配UnitConfiguration.Configurations中的项目,会匹配UIDBCUIndexIndexBCUIndex不配置或为0或(线缆信息不包含BCUIndex且TopoNode不能明确BCUIndex)时忽略BCUIndex的匹配。Index不配置或为0或线缆信息不包含Index时忽略Index的匹配,之后进行TargetPortID的匹配,根据线缆读出的对端线缆ID去匹配,如线缆读取33、49,就会匹配TargetPortID = [33, 49]的项(顺序无关);如果一条都匹配不上,匹配配置了default: true的项;最后如果没有配置default: true,匹配第一项。
  4. TargetPortIDSrcPortName的数组元素是一一对应的。例如下图UnitConfiguration_IEU1中TargetPortID的33、34、49分别对应A2a、A2c、A3a。
  5. UnitConfiguration的SrcPortName,找到BCU下行连接器里的Ports数组里的元素。BCU下行连接器的PortsUpstreamResources是一一对应的,进而找到对应的Serdes。注意如果有ActualResourceOrder字段,就是PortsActualResourceOrder是一一对应的。
  6. PortID的值是对应的Serdes对象的Device数组里的最小值。比如下图的A3a,向上找Serdes中的Device是4,因此下行的PcieAddrInfo的PortID也是4,即PcieAddrInfo_1.PortID=4槽位。
  7. 注意如果PcieAddrInfo_2槽位的宽度是X8,那么A2a对应的PortID就是10。因为A2a对应的Serdes是ActualResourceOrder里的SerDes_0_4_A,宽度是X8。

5. 计算rootBDF

rootBDF和PortId的关系由带内给出,通过上面提到的硬件提供表格给出了示意,对于1630处理器的机型,对应表直接写在代码中,其他机型配置在BDFConfig里。

BDFConfig配置示意:

json
"BDFConfig_cpu0": {
	"Type": "RootBDF",
    "SocketId": 0,
    "BDFConfigs": [
    	{
        	"PortId": 4,
            "Bus": 1,
            "ControllerIndex": 0,
            "Device": 4
        },
        {
        	"PortId": 5,
            "Bus": 1,
            "ControllerIndex": 0,
            "Device": 5
        }
    ]
}

6. 常见配置

BCU的CSR一般已经固定,IEU/EXU/SEU的CSR虽然看起来配置很多,但是大体有以下三种类型,主要是上下行连接器的区别,列出如下:

6.1 1个UBC/UBCDD对应1个槽位

IEU上行端口对应1个x16的Port,下行端口为x16。

IEU上行端口:

"BusinessConnector_1": {
      "Name": "Up_1",
      "Direction": "Upstream",
      "Slot": 1,
      "LinkWidth": "X16",
      "MaxLinkRate": "PCIe5.0",
      "ConnectorType": "UBCDD",
      "Ports": [
        {
          "Name": "Port1",
          "ID": 17,
          "Offset": 0,
          "Width": 16
        }
      ]
    },

IEU下行端口:

"BusinessConnector_2": {
      "Name": "Down_1",
      "Direction": "Downstream",
      "Slot": 1,
      "LinkWidth": "X16",
      "MaxLinkRate": "PCIe 5.0",
      "ConnectorType": "PCIe CEM",
      "UpstreamResources": [
        {
          "Name": "Up_1",
          "ID": 255,
          "Offset": 0,
          "Width": 16
        }
      ],
      "RefMgmtConnector": "#/Connector_PCIE_1",
      "RefPCIeAddrInfo": "#/PcieAddrInfo_1"
    },

6.2 1个UBCDD分为多个UBC口,对应多个槽位

IEU上行端口有2个x8的Port,下行端口为2个x8。

IEU上行端口 :

json
"BusinessConnector_1": {
         "Name": "Up_1",
         "Direction": "Upstream",
         "Slot": 1,
         "LinkWidth": "X16",
         "MaxLinkRate": "PCIe5.0",
         "ConnectorType": "UBCDD",
         "Ports": [
           {
             "Name": "Port1",
             "ID": 17,
             "Offset": 0,
             "Width": 8
           },
           {
             "Name": "Port2",
             "ID": 33,
             "Offset": 8,
             "Width": 8
           }
         ]
       },

IEU下行端口:

json
"BusinessConnector_2": {
      "Name": "Down_1",
      "Direction": "Downstream",
      "Slot": 1,
      "LinkWidth": "X8",
      "MaxLinkRate": "PCIe 5.0",
      "ConnectorType": "PCIe CEM",
      "UpstreamResources": [
        {
          "Name": "Up_1",
          "ID": 255,
          "Offset": 0,
          "Width": 8
        }
      ],
      "RefMgmtConnector": "#/Connector_PCIE_1",
      "RefPCIeAddrInfo": "#/PcieAddrInfo_1"
    },

"BusinessConnector_3": {
      "Name": "Down_2",
      "Direction": "Downstream",
      "Slot": 2,
      "LinkWidth": "X8",
      "MaxLinkRate": "PCIe 5.0",
      "ConnectorType": "PCIe CEM",
      "UpstreamResources": [
        {
          "Name": "Up_1",
          "ID": 255,
          "Offset": 8,
          "Width": 8
        }
      ],
      "RefMgmtConnector": "#/Connector_PCIE_2",
      "RefPCIeAddrInfo": "#/PcieAddrInfo_2"
    },

6.3 多个UBC口合成1个槽位

两个x8的Port的对应两个IEU上行端口,对应一个下行x16端口。

IEU上行端口 :

json
"BusinessConnector_1": {
      "Name": "Up_1",
      "Direction": "Upstream",
      "Slot": 3,
      "LinkWidth": "X8",
      "MaxLinkRate": "PCIe 5.0",
      "ConnectorType": "UBC",
      "Ports": [
        {
          "Name": "Port1",
          "ID": 33,
          "Offset": 0,
          "Width": 8
        }
      ],
      "Port2LinkInfo": "UBC3"
    },

"BusinessConnector_2": {
      "Name": "Up_2",
      "Direction": "Upstream",
      "Slot": 4,
      "LinkWidth": "X8",
      "MaxLinkRate": "PCIe 5.0",
      "ConnectorType": "UBC",
      "Ports": [
        {
          "Name": "Port2",
          "ID": 34,
          "Offset": 0,
          "Width": 8
        }
      ],
      "Port2LinkInfo": "UBC4"
    },

IEU下行端口:

json
"BusinessConnector_3": {
      "Name": "Down_1",
      "Direction": "Downstream",
      "Slot": 2,
      "LinkWidth": "X16",
      "MaxLinkRate": "PCIe 5.0",
      "ConnectorType": "PCIe CEM",
      "UpstreamResources": [
        {
          "Name": "Up_1",
          "ID": 255,
          "Offset": 0,
          "Width": 8
        },
        {
          "Name": "Up_2",
          "ID": 255,
          "Offset": 0,
          "Width": 8
        }
      ],
      "RefMgmtConnector": "#/Connector_PCIE_SLOT2",
      "RefMgmtConnectorTianChi": "#/Connector_PCIE_SLOT2TianChi",
      "RefPCIeAddrInfo": "#/PcieAddrInfo_2"
    },

7. Q&A

Q: 拓扑建立和线缆检测有什么异同?

A:拓扑建立的目的是确定一种插法,因此当只配置了一种插法时,不关心实际线缆信息的结果,有多种时才会去匹配当前插法,若没有匹配上也会用默认插法(没有默认时用第一种插法)去进行。而线缆检测如果没有完全匹配就会告警(有部分特殊情况)。

Q: 我已经确定了端口的BDF、PortID和SlotID,是否可以直接配死?

A: 可以,如果不需要使用BDF上报的方式进行卡加载,只需要写死PcieAddrInfo各个属性,然后保证卡的Connector的DeviceType和SlotID和PcieAddrInfo对应(需要手动设置Presence=1,或直接把PCIeDevice写在SR中),卡就能加载并正常关联上PcieAddrInfo。如果希望使用上报BDF的方式进行卡加载,需要配置下行连接器、PcieAddrInfo和Connector,PcieAddrInfo中的PortID会被置为对应Configurations下对应项目的Device[下行连接器Slot],SlotID为对应项目的Slot[下行连接器Slot]。

Q: IEU有三个下行连接器,但是实际接法只允许两个槽位,我又不想改CSR,应该如何配置

A: 将PSR中对应配置的Slot配置为255,如"Slot": [255, 2, 3]

Q: 我遇到了概率性卡不加载,发现一种类型设备的BDF上报到另外一种设备的指令中了(如PCIe卡的BDF作为OCP上报)

A: 检查是否发生了PortID冲突(对应丝印中的RootPortDeviceId),RootPortDeviceId和SocketId组成的索引应该在丝印的PCIeSilk项目中唯一。

Q: 我遇到了同槽位的卡只加载上一张

A: 检查是否SlotID冲突,注意SlotId和DeviceType组成的索引在丝印文件的PCIeSilk项目中才是唯一的,单SlotId不唯一。

Q: Riser卡的槽位是怎么确定的?

A: 对于低速线和高速线分开的情况,槽位就是低速线配置的槽位。对于低速线和高速线合一的情况,当低速线在位时,会去PSR中匹配Connector中的源端线缆名称(如Connector_A1a为A1a),找到有A1a的UnitConfiguration,用该UnitConfiguration的SlotNumber作为组件的SlotNumber(注意这种情况下,若两个UnitConfiguration都有A1a,可能会导致SlotNumber不确定)。

Q: 拓扑建立会使用那种配置进行?

A:如果当前UnitConfiguration只有一种配置,选择这种;如果有多种配置,会读取一次线缆信息之后使用完全匹配上的;如果没有完全匹配上的,选择default = true的;如果没有配置default,选择第一个。