【Q】CPU、内存信息是怎样加载的?
概述
CPU的静态信息包括家族、型号、ID、序列号、部件号等,内存的静态信息包括容量、位宽、厂商、序列号等,这些静态信息来自BIOS上报的SMBIOS信息
SMBIOS信息的上报通常发生在BIOS完成对系统硬件的基本检测和初始化之后,在操作系统加载之前
SMBIOS协议简介
SMBIOS是DMTF制定的开放标准,用于规范主板及系统厂商如何存储和管理产品信息。通过统一格式让厂商在BIOS中记录硬件信息,便于管理软件获取硬件配置数据
当前openUBMC支持对Processor Information(4)/Cache Informaiton(7)/Physical Memory Array(16)/Memory Device(17)部分字段解析上报
详细流程
1、BIOS通过IPMI命令ReadFileFromBmc从BMC读取内存丝印信息,包含Memory对象的CpuId、LogicalChannelId、DimmId等属性
2、BIOS将丝印信息填充到SMBIOS表格中,通过IPMI命令WriteSmbiosData上报给BMC,写成SMBIOS_CONF文件
3、bios组件读取这个文件并解析,最终将CPU、内存信息更新到资源协作路径上。内存信息通过丝印信息来匹配Memory对象,CPU信息通过LogicalLd来匹配CPU对象
通过SMBIOS获取的信息
CPU对象 SMBIOS字段映射表
| SMBIOS字段 | 资源树属性 | 转换逻辑 |
|---|---|---|
processor_manufacturer | Manufacturer | 直接赋值 |
processor_family | Family | 通过 g_proc_models 表映射为家族名称 |
processor_family_2 | Family | 当 processor_family 为 0xFE 时使用此字段 |
max_speed | MaxSpeedMHz | 直接赋值(MHz) |
current_speed | CurrentSpeedMHz | 直接赋值(MHz) |
external_clock | BaseSpeedMHz | 直接赋值(MHz) |
processor_version | Model | 直接赋值 |
part_number | PartNumber | 非 "To be filled by O.E.M." 时赋值,否则设为 "Unknown" |
part_number | InventoryPartNumber | 同上 |
processor_id | ProcessorID | 直接赋值 |
processor_id | ProcessorIDString | 通过 get_processor_id() 函数转换 |
processor_type | ProcessorType | 转为字符串 |
serial_number | SN | 非 "To be filled by O.E.M." 时赋值,否则设为 "Unknown" |
serial_number | SerialNumber | 同 SN |
processor_manufacturer | InventoryManufacturer | 同 Manufacturer |
processor_version | InventoryModel | 同 Model |
socket_designation | SocketDesignation | 直接赋值 |
core_count | TotalCores | 直接赋值,为 0xFF 时使用 core_count_2 |
core_count_2 | TotalCores | 当 core_count 为 0xFF 时使用 |
core_count | TotalEnabledCores | 通过 get_enabled_cores() 函数计算 |
thread_count | TotalThreads | 直接赋值,为 0xFF 时使用 thread_count_2 |
thread_count_2 | TotalThreads | 当 thread_count 为 0xFF 时使用 |
processor_characteristics | Characteristics | 直接赋值 |
thread_enabled | TotalEnabledThreads | 直接赋值,失败时默认为0 |
CPU特殊值处理
| 场景 | 转换逻辑 |
|---|---|
part_number 为 "To be filled by O.E.M." | → "Unknown" |
serial_number 为 "To be filled by O.E.M." | → "Unknown" |
processor_family 为 0xFE | 使用 processor_family_2 字段 |
core_count 为 0xFF | 使用 core_count_2 字段 |
thread_count 为 0xFF | 使用 thread_count_2 字段 |
内存对象 SMBIOS字段映射表
| SMBIOS字段 | 资源树属性 | 转换逻辑 |
|---|---|---|
speed | AllowedSpeedsMHz | 直接赋值(MT/s) |
(通过 get_base_module_type) | BaseModuleType | 类型转换函数 |
size | CapacityMiB | 小于 0x7fff 时直接赋值 |
extended_size | CapacityMiB | 当 size >= 0x7fff 时使用此字段 |
data_width | DataWidthBits | 直接赋值(bit) |
firmware_version | FirmwareRevision | 通过 get_firm_ware_revision() 转换 |
manufacturer | Manufacturer | 直接赋值 |
(通过 get_memory_device_type) | MemoryDeviceType | 类型转换函数 |
memory_subsystem_controller_manufacturer_id | MemorySubsystemControllerManufacturerID | 转为字符串 |
memory_subsystem_controller_product_id | MemorySubsystemControllerProductID | 转为字符串 |
type_detail | MemoryType | 通过 :tostring() 方法转换 |
configured_memory_speed | OperatingSpeedMhz | 直接赋值(MT/s) |
part_number | PartNumber | 通过 trim() 去除首尾空格 |
part_number | OriginalPartNumber | 同上 |
attributes | RankCount | 通过 :rank() 方法获取 |
serial_number | SerialNumber | "NO DIMM" 时设为 "N/A",否则直接赋值 |
(通过 get_memory_presence) | Presence | 根据厂商信息判断,详见下方说明 |
minimum_voltage | MinVoltageMillivolt | 直接赋值(mV) |
(通过 get_manufacturing_date) | ManufacturingDate | 日期格式转换 |
(通过 get_bom_num) | BOM | BOM号转换 |
(通过 get_manufacturer_id) | ManufacturerID | 厂商ID转换 |
(通过 get_memory_type) | Technology | 内存技术类型转换 |
内存Presence计算逻辑
内存的 Presence 属性由 get_memory_presence(smbios_entry) 函数计算,核心逻辑如下:
(Memory.Manufacturer ~= 'NO DIMM' and Memory.Manufacturer ~= 'empty') and 1 or 0即:当厂商信息既不是 "NO DIMM" 也不是 "empty" 时,Presence 设为 1(存在),否则设为 0(不存在)。
内存特殊值处理
| 场景 | 转换逻辑 |
|---|---|
serial_number 为 "NO DIMM" | → "N/A" |
size >= 0x7fff | 使用 extended_size 字段(支持64GB以上内存) |
Presence == 0(不在位) | 清空 Inventory* 系列属性 |
内存Inventory属性(Presence=1时)
当内存 Presence 为 1 时,以下属性会被填充:
| 属性 | 值来源 |
|---|---|
AssetType | 固定为 "Memory" |
InventoryManufacturer | 同 Manufacturer |
InventorySerialNumber | 同 SerialNumber |
InventoryPartNumber | 同 PartNumber |
AssetName | Position + DimmName 拼接 |
FirmwareVersion | 固定为 "N/A" |
PCBVersion | 固定为 "N/A" |
AssetTag | 固定为 "N/A" |
ManufactureDate | 固定为 "N/A" |
Slot | 同 DimmName |
Model | 固定为 "N/A" |
当 Presence 为 0 时,上述 Inventory 属性会被清空。
上述SMBIOS文件可以在一键收集中找到,路径为 dump_info\AppDump\bios\1\SMBIOS_CONF,具体解析方式可参考 /bios/include/bios/smbios/memory_decoder.lua 和 /bios/include/bios/smbios/processor_decoder.lua。
【Q】内存占用率获取不到
当下内存的占用率可以通过多种途径查询,仅列出最便捷的几种查询方法
1、通过web首页的系统监控页第三栏内存查看
2、通过redfish URL `https://device_ip/redfish/v1/Systems/1/MemorySummary/MemoryMetrics` 查询 `MemoryMetrics` 资源下的 `BandwidthPercent` 属性
3、通过命令 `mdbctl lsprop MemoryMetrics_1_010101` 查询资源协作接口 `bmc.kepler.Systems.Memory.MemoryMetrics` 下的 `BandwidthPercent` 属性当下内存的占用率获取依赖bma服务,该服务的使用方式可以参考社区文档:HOST代理管理常见问题指南。
当内存占用率获取不到时,你首先可以查看如下信息
busctl --user introspect bmc.kepler.host_agent /bmc/kepler/Systems/1/Sms
bmc.kepler.Systems.Sms (查看该接口下的 Registered 属性)
Registered
bmc.kepler.Systems.Sms.SmsStatus (查看该接口下的 State 属性)
State
busctl --user introspect bmc.kepler.host_agent /bmc/kepler/Systems/1/Sms/1/ComputerSystem/Systems/1/Summary
bmc.kepler.sms.redfish.Memory (查看该接口下的 MemUsage 属性)
MemUsage查看命令回显的 Registered 是否为 true,State 是否为 0,MemUsage 的值是否为小于100的非负数。若上述条件均为是,则可以认为bma正常运行且获取到了内存占用率。现在对于compute组件进行相应的排查,查看是否有如下形式notice级别的日志打印
[system1]sms registered is true
[system1]sms status is 0如果未出现上述形式的日志打印,可以尝试升级openubmc版本至20251230之后的版本。
【Q】CPU占用率获取不到
CPU占用率的来源相比于内存占用率多了从bma获取的方式,因此在内存占用率获取异常时,CPU占用率是有可能正常获取到的。当下cpu占用率的查看方式同样有多种,此处列出最便捷的几种方式,以供参考
1、通过web首页的系统监控页第二栏CPU查看
2、通过redfish URL `https://device_ip/redfish/v1/Systems/1/ProcessorSummary/ProcessorMetrics` 查询 `ProcessorMetrics` 资源下的 `BandwidthPercent` 属性
3、通过命令 `mdbctl lsprop CPUMetrics_1_010101` 查询资源协作接口 `bmc.kepler.Systems.Processor.ProcessorMetrics` 下的 `BandwidthPercent` 属性对于CPU占用率获取不到主要分为一下两种情况进行分析 1.内存占用率获取成功,CPU占用率获取失败 2.内存占用率获取失败,CPU占用率获取失败 对于第一种情况,请通过如下命令查看
busctl --user introspect bmc.kepler.host_agent /bmc/kepler/Systems/1/Sms/1/ComputerSystem/Systems/1/Summary
bmc.kepler.sms.redfish.Processor (查看该接口下的 TotalCPUUsage 属性)
TotalCPUUsage若TotalCPUUsage为255,可以明确在bma侧继续定位。若不为255,可以寻找compute组件的负责人进行后续的定位,目前无相关场景经验。
对于第二种情况,首先打开compute组件的debug级别日志开关,并查找相关日志进行定位。
mdbctl
attach compute
dloglevel debug
dlogtype local
update cpu usage fail, cc =若出现日志打印update cpu usage successfully, usage = 255,可以初步定位为带内返回数据有误。建议评估影响后尝试重启os。 若出现日志打印update cpu usage fail, cc = %d,依据具体的返回compcode以明确对应问题。这一情况也可以初步定位为带内异常,建议评估影响后尝试重启os。 若上述日志打印均未出现,请尝试回退BIOS版本,查看能否正常查询。
对于第二种情况,上述措施均致力于修复通过imu查询cpu占用率失败问题,实际更建议通过bma查询cpu占用率信息。
【Q】上电时出现内存对象SN变更告警
内存SN变更时会出现如下打印:
2026-02-03 20:11:24.024117 frudata NOTICE: sn_replace.lua(62): BoardSerialNumber changed from 80AD01240697D99125 to 8A91502511B008497D
2026-02-03 20:11:24.065079 frudata NOTICE: sn_replace.lua(62): BoardSerialNumber changed from 80AD01240697D7EEEC to 8A91502511B0084973若以上打印出现前,未进行内存的更换,则为误告警。内存对象的SN来源于BIOS基于BMC发送的丝印文件提供的SMBIOS文件。若出现以上情况,可以判定为BIOS上报的SNBIOS文件中的SN发生了变动。首先确认我们发送给带内的丝印文件是否有误,丝印文件位于一键收集日志的 dump_info\AppDump\bios\1\silkconfig.json 下,其中一个内存的丝印文件如下,并标注了其对应的内存对象的属性
{
"DimmId": 1, 内存对象 bmc.kepler.Systems.Memory 接口下的 DimmId 属性
"LogicalChannelId": 5, 内存对象 bmc.kepler.Systems.Memory 接口下的 LogicalChannelId 属性
"PhysicalChannelId": 3, 内存对象 bmc.kepler.Systems.Memory 接口下的 ChannelId 属性
"SocketId": 0, 内存对象 bmc.kepler.Systems.Memory 接口下的 CpuId 属性
"Silk": "DIMM031" 内存对象 bmc.kepler.Systems.Memory 接口下的 DimmName 属性
}通过查询日志文件 dump_info\AppDump\compute\mdb_info.log,可以发现不存在符合该条件的内存,即可判定为发送的丝印文件有误。一般而言丝印文件的传输是由BIOS在带内os起来后,主动要求BMC发送的,因此当BIOS在请求丝印文件时,若相关信息没有上树,则会出现发送的丝印文件错误的情况。更具体的流程可以参考这篇社区文档:丝印上报总结分享,该文档讲解得非常详细。
【Q】内存不加载
内存的加载流程整体分为两部分
- 硬件自发现的对象分发
- 依据丝印文件中的信息将对应内存对象
bmc.kepler.Systems.Memory接口下的Presence置为0/1
第一部分的相关流程,社区中有详细的文档介绍,此处不重复介绍。第二部分则依赖于BIOS提供的SMBIOS文件。具体的计算方式如下:
(Memory.Manufacturer ~= 'NO DIMM' and Memory.Manufacturer ~= 'empty') and 1 or 0其中 Memory.Manufacturer 为该内存对象 bmc.kepler.Systems.Memory接口下的 Manufacturer 属性。
【Q】NPU卡支持哪些属性?如何获取?
NPU卡通过标准SMBUS协议与MCU通信,获取各类属性信息。以下是各属性分类的详细说明:
| 属性名称 | 命令字(opcode) | 查询间隔 | 响应转换方法 | 数据类型 |
|---|---|---|---|---|
| McuFirmwareVersion | 0x0005 | 30s | string.format('%s.%s.%s', string.unpack('BBB', data)) | 版本字符串 |
| FirmwareVersion | 0x0607 | 30s | 直接返回字符串 | 版本字符串 |
| PowerWatts | 0x0004 | 2s | bs.new([[<<power:16>>]]):unpack(data, true).power | 整型(瓦特) |
| DeviceTemperature | 0x001D | 2s | 按传感器名称映射为 AiCoreTemp/HBMTemp/NimBusTemp/VRDChipTemp | 温度对象 |
| ChipTemperature | 0x0003 | 2s | bs.new([[<<chip_temp:16>>]]):unpack(data, true).chip_temp | 整型(摄氏度) |
| MemoryCapacityKiB | 未在配置中定义 | 30s | 每个芯片容量累加后除以1024转为MB | 整型(MiB) |
| MemoryBandWidth | 未在配置中定义 | 30s | 直接返回 | 整型 |
| SerialNumber | 通过Elabel获取 | 按需 | 来自Elabel的BoardSerialNumber | 字符串 |
| BoardID | 0x0F | 10s | bs.new([[<<board_id:16>>]]):unpack(data, true).board_id | 整型 |
| PcbVersion | 0x10 | 10s | string.format('.%c', val + 64) (A-Z) | 字符型 |
| SN | 通过SerialNumber获取 | 按需 | 与SerialNumber同步 | 字符串 |
| ErrorCode | 0x0002 | 按需 | 解析为错误码数组,0x00表示无错误 | 数组 |
| NPUFaultCode | 0x060D | 按需 | 4字节一组拼接为16进制字符串 | 数组 |
| ChipFaultDescription | - | 按需 | ErrorCode + NPUFaultCode 组合 | 字符串 |
| Capabilities | 0x00 | 30s | 解析为opcode列表(命令支持列表) | 表 |
| McuType | 0x90 | 按需 | 高低位拼接为vendor_id | 整型 |
| McuTime | 0x21 | 按需 | 时间戳同步 | 时间戳 |
| CurEccErrCount | 0x1F | 30s | {cur_single_err_count, cur_double_err_count} | 对象 |
| AggregateAndIsolatedCount | 0x681 | 30s | {single_total_count, double_total_count, single_isl_count, double_isl_count} | 对象 |
| RecordedIsolatedPagesCount | 0x68A | 30s | recorded_isolated_count | 整型 |
| ErrorLog | 0x000C | 按需 | 直接返回原始数据 | 二进制数据 |
| OperateLog | 0x26 | 按需 | 直接返回原始数据 | 二进制数据 |
| MaintenanceLog | 0x27 | 按需 | 直接返回原始数据 | 二进制数据 |
电子标签(Elabel)列表
| 属性名称 | 参数(arg) | 说明 |
|---|---|---|
| ChassisType | 0x10 | 机箱类型 |
| ChassisPartNumber | 0x11 | 机箱型号 |
| ChassisSerialNumber | 0x12 | 机箱序列号 |
| MfgDate | 0x20 | 生产日期(6字符时间戳) |
| BoardManufacturer | 0x21 | 板卡制造商 |
| BoardProductName | 0x22 | 板卡产品名称 |
| BoardSerialNumber | 0x23 | 板卡序列号 |
| BoardPartNumber | 0x24 | 板卡型号 |
| BoardFRUFileID | 0x25 | 板卡FRU文件ID |
| ManufacturerName | 0x30 | 制造商名称 |
| ProductName | 0x31 | 产品名称 |
| ProductPartNumber | 0x32 | 产品型号 |
| ProductVersion | 0x33 | 产品版本 |
| ProductSerialNumber | 0x34 | 产品序列号 |
| AssetTag | 0x35 | 资产标签 |
| ProductFRUFileID | 0x36 | 产品FRU文件ID |
| SystemManufacturerName | 0x60 | 系统制造商名称 |
| SystemProductName | 0x61 | 系统产品名称 |
| SystemVersion | 0x62 | 系统版本 |
| SystemSerialNumber | 0x63 | 系统序列号 |
DeviceTemperature 传感器映射表
| MCU传感器名称 | 映射属性名 |
|---|---|
| AICORE | AiCoreTemp |
| HBM | HBMTemp |
| PREDEV | NimBusTemp |
| VRDMAX | VRDChipTemp |
读取异常时的特殊值
| 常量 | 值 | 说明 |
|---|---|---|
| NA_READING | 0x4000 | 设备不在位或读不到值 |
| INVALID_READING | 0x8000 | 读取失败 |
Elabel获取方式: 通过 opcode 0x0015,根据 arg 参数区分不同属性 说明: MemoryCapacityKiB 为按芯片遍历获取,所有芯片容量累加后除以1024转为 MiB。 说明: SerialNumber 来自电子标签(Elabel)中的 BoardSerialNumber 字段。SN 属性通过 update_sn_by_std_smbus 方法获取并同步到资源树。 说明: ChipFaultDescription 由 ErrorCode 和 NPUFaultCode 组合而成,用于描述芯片故障信息。
【Q】不同型号NPU卡的配置差异
不同型号的NPU卡在传感器映射、芯片数量等方面存在差异:
表 传感器映射差异
| 型号 | 传感器名称 | 映射属性名 |
|---|---|---|
| Atlas 300I A2 | AICORE, HBM, PREDEV, VRDMAX | AiCoreTemp, HBMTemp, NimBusTemp, VRDChipTemp |
| Atlas 300I Pro | LM75B_TE, LM75A_TE, AICORE | InletTemperatureCelsius, OutletTemperatureCelsius, Core0TemperatureCelsius |
| Atlas 300I Duo | T_LM75B, T_LM75A, T_CORE_M, T_CORE_S | InletTemperatureCelsius, OutletTemperatureCelsius, Core0TemperatureCelsius, Core1TemperatureCelsius |
表 芯片数量差异
| 型号 | 芯片数量 |
|---|---|
| Atlas 300I A2 | 1 |
| Atlas 300I Pro | 1 |
| Atlas 300I Duo | 2 |
【Q】NPU卡常见问题排查
| 问题 | 可能原因 | 解决方案 |
|---|---|---|
| 返回NA_READING (0x4000) | 设备不在位或通信异常 | 检查PCIe连接和MCU通信 |
| 返回INVALID_READING (0x8000) | 读取超时或协议错误 | 检查网络延迟和MCU状态 |
【Q】CPU信息获取方式
CPU的动态信息使用IPMI命令向IMU查询,compute组件负责定期采集并更新到资源树。以下是各属性对应的查询间隔、数据转换规则和资源树属性。
CPU查询命令汇总表
| 资源树接口 | 属性 | 查询间隔 | 数据转换 | 特殊值 |
|---|---|---|---|---|
| bmc.kepler.Systems.Processor | TemperatureCelsius | 5s | tonumber(string.format('%d.%d', TempHi, TempLo)) | 0x8000(失败)、0x4000(不在位) |
| bmc.kepler.Systems.Processor | PowerWatt | 20s | 直接返回整数值(瓦特) | 0x0000(连续失败10次) |
| bmc.kepler.Systems.Processor | PowerWatts | 20s | 直接返回整数值(瓦特) | - |
| bmc.kepler.Systems.Processor | MaxMemoryTSensorTemperatureCelsius | 5s | 取所有TSensor温度中的最大值 | 0x3FFF(查询失败)、0x7FFF(查询成功但温度值读取失败)、0x4000(不在位) |
| bmc.kepler.Systems.Processor | MaxMemoryTemperatureCelsius | 5s | 取所有DIMM温度中的最大值 | 0x8000(失败)、0x4000(不在位) |
| bmc.kepler.Systems.Processor | MaxMemoryTemperatureName | 5s | 返回最高温度对应的DIMM名称 | "N/A" |
| bmc.kepler.Systems.Processor.ProcessorMetrics | ConsumedPowerWatt | 20s | 所有CPU功耗累加值(瓦特) | - |
| bmc.kepler.Systems.Processor.ProcessorMetrics | BandwidthPercent | 20s | 小数位为0取整数值,否则整数位+1;超100%返回0xFF | 0xFF |
【Q】内存信息获取方式
内存的动态信息通过IMU使用IPMI命令获取,与CPU共享相同的命令通道。以下是各属性对应的查询间隔、数据转换规则和资源树属性。
内存查询命令汇总表
| 资源树接口 | 属性 | 查询间隔 | 数据转换 | 特殊值 |
|---|---|---|---|---|
| bmc.kepler.Systems.Memory | TemperatureCelsius | 5s | 遍历DIMM温度,保留小数 | 0x8000(失败)、0x4000(不在位) |
| bmc.kepler.Systems.Memory | LogicTemperatureCelsius | 5s | 取所有Logic区温度中的最大值 | 0x8000(失败)、0x4000(不在位) |
| bmc.kepler.Systems.Memory | DRAMTemperatureCelsius | 5s | 取所有DRAM区温度中的最大值 | 0x8000(失败)、0x4000(不在位) |
| bmc.kepler.Systems.Memory.MemoryMetrics | ConsumedPowerWatt | 20s | 直接返回整数值(瓦特) | 0xFFFFFFFF(失败) |
| bmc.kepler.Systems.Memory.MemoryMetrics | BandwidthPercent | 20s | 直接返回整数值(百分比) | 0xFF |
| bmc.kepler.Systems.Memory.MemoryMetrics | SystemMemoryBuffersGiB | 20s | 直接返回整数值(GiB) | 0 |
| bmc.kepler.Systems.Memory.MemoryMetrics | SystemMemoryCachedGiB | 20s | 直接返回整数值(GiB) | 0 |
| bmc.kepler.Systems.Memory.MemoryMetrics | TotalSystemMemoryGiB | 20s | 直接返回整数值(GiB) | 0 |