硬件自发现
更新时间:2024/12/19
在Gitcode上查看源码

在openUBMC中我们能够通过MDS数据模型和CSR硬件自描述的结合完成业务组件对硬件的管理。但从硬件管理的全貌来看,固定的配置并不利于多样化的硬件适配,为此openUBMC提出来硬件自发现框架,以实现对硬件组件灵活的自发现管理。

硬件自发现框架

硬件自发现归属于openUBMC的框架,属于南向硬件访问的一部分,依托hwproxy硬件代理,实现对硬件组件的自发现管理,对业务提供对象数据以及数据的引用、同步关系。

硬件自发现业务流程

  • 硬件自发现启动时从指定路径下搜索MDS描述文件,获取类、属性及资源协作接口定义信息,构造类与组件映射关系;
  • 硬件自发现访问系统flash区,获取root.sr文件,此文件中描述了产品芯片的链路拓扑信息;获取platform.sr,此文件描述了软件配置对象信息;
  • 硬件自发现解析对象信息,根据类与组件映射关系,标识对象归属组件,并以对象组的方式发布对象数据;
  • 其他组件收到对象组发布信号后,通过自发现接口获取归属组件的对象信息,执行对象添加操作,对外提供业务服务;硬件代理在收到对象发布信号后,还需通过硬件自发现接口获取拓扑链路信息,并建立硬件拓扑链路;
  • 硬件自发现加载连接器对象,并根据连接器的识别模式、在位状态等执行下级组件的并发发现,包括读取Eeprom数据、获取sr文件数据、解析对象数据、发布对象组,并重复上述自发现流程。

CSR解析流程

解析、校验、解压缩CSR数据是一个循环的流程,该过程采用了层级加载的方式进行。如下图所示:

当前openUBMC的插卡和扩展板(EXU)直接连接,再有EXU连接基础板(BCU)、风扇板(CLU)、硬盘背板(SEU)、板载网卡(NIC)等对象。其次,BCU会连接Riser卡,在Riser卡上插有各种PCIe卡。这些连接方式,我们通过具象的Connector表示板卡和板卡之间的连接。自发现过程也会按照该加载顺序依次进行。

在完成一个CSR的解析后,会按照类与组件的映射关系,将对应数据放置于ObjectGroup中,并以Owner的方式标记此类属于哪个组件。在该CSR对应的ObjectGroup组装完成后就会分发给各个业务app。

随后就会开启下一层级的自发现,即检查该CSR中定义的Connector对象。Connector的存在定义了下一层级的硬件CSR的类型,并同时定义了其来源。对于天池组件而言,需要使用特定的Chip读取存储在硬件Eeprom中的CSR文件,并同时使用bom_id_auxid获取打包的CSR文件,最后对多版本的CSR文件进行版本比较,以确定最新版本的CSR文件。

确定CSR文件后就开启下一阶段的解析、校验、解压缩。

自描述对象重命名规则

为保证SR定义对象名称全局唯一,需要对每个(SR)硬件组件中自描述对象进行重命名,重命名后的对象名称由SR定义的对象名+_${Position}后缀组成。例如:

NetworkPort_1_010105
ExpBoard_1_0101
RiserCard_1_01010103

其中Position后缀是由多个两位十六进制数组合而成;每个两位十六进制数,分表代表不同SR中对应Connector的Position属性。

第一个两位十六进制数,是sr数据解析的入口,有固定的命名,“00”表示platform.sr,“01”表示root.sr。

连接器Connector对象的GroupPosition即下级组件(SR)对象的Position后缀,是通过连接器对象的Position后缀(上级Connector的GroupPosition属性)和SR文件中配置的连接器对象Position属性拼接而来。例如:配置在BCU中的Connector的Position为通过EXU(SR对应的Position为0101)中配置的Connector_BCU_1的Postion1,拼接后得到BCU中的Connector的Position为010101

对外接口

硬件自发现主要提供了对象组发布后的归属指定组件的对象数据获取接口以及拓扑链路的获取接口。

硬件自发现会将每个(SR)硬件组件的对象以ObjectGroup的方式呈现在资源协作接口上,ID以Position命名。例如:

├─/bmc/kepler/ObjectGroup
│ ├─/bmc/kepler/ObjectGroup/00
│ ├─/bmc/kepler/ObjectGroup/01
│ ├─/bmc/kepler/ObjectGroup/0101
│ ├─ ...
└─/bmc/kepler/hwdiscovery

ObjectGroup对象组对外提供GetObjects接口,输入参数:a{ss}(上下文参数)s(Owner,即获取指定对象所有者App名称),输出参数: s(组件Position)a{ssss(类名、对象名、对象属性、扩展属性)}u(组件对象生命周期Id标识)。例如想要查看分发给network_adapter的ObjectGroup,通过如下命令可以获取:

shell
busctl --user call bmc.kepler.hwdiscovery /bmc/kepler/ObjectGroup/010109 bmc.kepler.ObjectGroup GetObjects 'a{ss}s' 0 network_adapter

其中010109是分发给network_adapter的网卡CSR对应的position,具体可以通过busctl --user tree bmc.kepler.network_adapter查看资源协作接口下的对象position。a{ss}s后的0代表上下文参数数量为0,不需要传入上下文参数。network_adapter是查看ObjectGroup分发给组件network_adapter的对象信息的筛选条件。

通过此方式也能够获取分发给hwdiscovery的ObjectGroup,通过如下命令获取:

shell
busctl --user call bmc.kepler.hwdiscovery /bmc/kepler/ObjectGroup/01 bmc.kepler.ObjectGroup GetObjects 'a{ss}s' 0 'hwdiscovery'

sa(ssss)u "01" 2 "Connector" "Connector_xxx_01" "{\"Position\":2,\"ChassisId\":\"1\",\"ManagerId\":\"1\",\"SystemId\":1,\"AuxId\":\"0\",\"Buses\":[],\"Id\":\"Sensor\",\"Presence\":1,\"Slot\":99,\"GroupId\":4,\"GroupPosition\":\"0102\",\"IdentifyMode\":2,\"Bom\":\"9999\"}" "{\"Path\":\"\\/bmc\\/kepler\\/Connector\\/Connector_Sensor_01\",\"Position\":\"01\",\"ChassisId\":\"\",\"ManagerId\":\"1\",\"SystemId\":0,\"Framework\":true}" "Connector" "Connector_xxx_1_01" "{ ... } 1
``
其他组件的硬件信息获取也类似。除此之外,也可以通过`introspect`查看ObjectGroup的内置属性。
shell
busctl --user introspect bmc.kepler.hwdiscovery /bmc/kepler/ObjectGroup/01

(output)
NAME                                TYPE      SIGNATURE   RESULT/VALUE                             FLAGS
bmc.kepler.Object.Properties        interface -           -                                        -
.GetAllWithContext                  method    a{ss}s      a{sv}                                    -
.GetOptions                         method    a{ss}ss     a{ss}                                    -
.GetPropertiesByNames               method    a{ss}sas    a{sv}a{sv}                               -
.GetPropertiesByOptions             method    a{ss}sa{ss} as                                       -
.GetWithContext                     method    a{ss}ss     v                                        -
.SetWithContext                     method    a{ss}ssv    -                                        -
.ClassName                          property  s           "ObjectGroup"                            emits-change
.ObjectIdentifier                   property  (ysss)      0 "" "" ""                               emits-change
.ObjectName                         property  s           "ObjectGroup_01"                         emits-change
bmc.kepler.ObjectGroup              interface -           -                                        -
.GetObjects                         method    a{ss}s      sa(ssss)u                                -
.GetTopology                        method    a{ss}       s                                        -
.OnlineTimestamp                    property  t           99999                                    emits-change
.Owners                             property  as          5 "hwdiscovery" "general_hardware"       emits-change
.Position                           property  s           "01"                                     emits-change
org.freedesktop.DBus.Introspectable interface -           -                                        -
.Introspect                         method    -           s                                        -
org.freedesktop.DBus.ObjectManager  interface -           -                                        -
.GetManagedObjects                  method    -           a{oa{sa{sv}}}                            -
org.freedesktop.DBus.Peer           interface -           -                                        -
.GetMachineId                       method    -           s                                        -
.Ping                               method    -           -                                        -
org.freedesktop.DBus.Properties     interface -           -                                        -
.Get                                method    ss          v                                        -
.GetAll                             method    s           a{sv}                                    -
.Set                                method    ssv         -                                        -
.PropertiesChanged                  signal    sa{sv}as    -                                        -

业务侧对象获取

在model.json中定义的MDS对象能够具备对外能力,但其中有的属性,定义了其属性来源是CSR。例如:

json
"SlotNumber":{
    "usage":[
        "CSR"
    ]
}

关于MDS的定义请参考《MDS》

在业务侧,有两种方式能够完成注册对象的分发。一种是object_manage方式,能够由开发者自定义业务流程;另一种是ORM方式,这种方式继承了object_manage的优势,同时还支持了数据库索引模式。

object_manage

可以通过object_manage注册对象的分发。例如在general_hardware中加载GPU,在app.lua中使用如下定义:

lua
local object_manage = require 'mc.mdb.object_manage'

function gen_hw_app:init_on_add_object()
    object_manage.on_add_object(self.bus, function(class_name, object, position)  -- object_manage注册框架
        self.gpu_service.on_add_object(class_name, object, position) --对象接收处理流程
    end) 
end

object_manage注册时,有三个参数传入,分别时class_nameobjectposition。这三个参数由硬件自发现分发而来。

class_name: CSR中定义的Object对象,通常以class_name + index的方式定义,例如GPU_1,所以对应的class_nameGPU

object: 该对象是由硬件自发现分发而来,其内值可以在对应的ObjectGroup中找到。

position: ObjectGroup分发时对应的position。

在gpu_service中对on_add_object完成对对象接收的处理流程:

lua
function gpu_service:on_add_object(class_name, object, position)
    local switch = {
        ['GPU'] = function ()
            self:add_object(object, position)
        end
    }

    if switch[class_name] then
        switch[class_name]()
        log:info('[gpu_service] Add object, Class: %s', class_name)
    end
end

function gpu_service:add_object(obj, position)
    local gpu = gpu_object.new_for_csr(obj, position)
    table.insert(self.gpu_collection, gpu)
    ...
end

在业务对象接收的处理流程,使用switch的方式对class_name进行筛选,并在gpu_object.new_for_csr中完成对obj对象的解析,可以通过obj.property的方式获取obj中的属性。并最终将gpu对象添加至self.gpu_collection中进行统一的管理。在业务处理过程中,self.gpu_collection中对象的改变会在资源协作接口上体现。

ORM

ORM使用时,需要在model.json中申明对象的tableName,例如在network_adapter中定义了:

json
"NetworkAdapter": {
    "tableName": "t_network_adapter",
    "tableType": "PoweroffPer",
    ...
    "interfaces": {
        "bmc.kepler.Systems.NetworkAdapter": {
            "properties": {
                "ID": {
                    "primaryKey": true
                },
                "SystemID": {
                    "usage": [
                        "CSR"
                    ]
                },
                ...
            }
        }
    }
}

在业务侧,需要在app.lua中定义orm_object_manage的使用。例如:

lua
local c_object_manage = require 'mc.orm.object_manage'

function app:init()
    ...
    self.object_manage = c_object_manage.new(self.db, self.bus)
    self.objcet_mage.app = app
    self.object_manage.per_db = self.db
    ...
    self.object_manage:start() -- 开启orm自发现对象分发
end

在业务侧,需要对orm object的一个方法进行override。例如network_adapter中,network_adapter对象的定义:

lua
function c_network_adapter.create_mdb_object(value)
    local app = c_object_manage.get_instance().app
    return app:CreateNetworkAdapter(1, value.ID, function (obj)  -- 资源对象创建
        log:debug("create network adapter id(%s), nodeid(%s), type(%d), RootBDF(%s)",
            value.ID, value.NodeId, value.Type, value.RootBDF)
        obj.ID = value.ID
        obj.NodeId = value.NodeId
        obj.Type = value.Type
        obj.RootBDF = value.RootBDF
    end)
end

其中obj为自发现分发的对象,value为数据库中的持久化信息。通过此方式,可以在创建对象时给资源对象进行一次预处理,修改其中的属性值。

在业务中,如果需要嗲用ORM对象中的某个方法,只需要使用collection.find的方法进行查询即可。更多MRO数据库的请参考《ORM对象管理框架使用指南》