general_hardware组件GPU监控线程异常中断问题

背景
客户在OS侧跑GPU压力测试,发现BMC这边的GPU温度不再更新:OS侧看GPU已经90多度了,BMC这边显示的还是40度。

定位过程
在BMC后台通过tracechip查看GPU芯片访问数据,发现没有任何打印;
查看log文件,发现如下打印:

环境上8张GPU,其中七张数据不再更新,与这里异常退出的线程数匹配。这里打印是log接口接收了预期之外的参数?没有生成core_dump文件,也没办法从这个打印中获取有用的定位信息,请问应该如何继续定位?

答案内容由AI生成,仅供参考:

:puzzle_piece: 五、结论与总结

项目 分析结果
根本原因 极可能是 GPU 温度读取返回 nil,而日志模块未做防空处理,导致 format_msg 报错,协程异常退出
传播机制 多个 GPU 监控线程同时失败 → 所有温度更新中断
触发条件 OS 层 GPU 高负载 → 可能引起 SMBus 通信异常 → 读取失败 → 返回 nil
责任组件 general_hardware 中 GPU 监控逻辑缺乏异常容忍机制
解决方案方向
  1. 所有日志参数增加 nil 判断;
  2. 使用 pcall 包裹采集逻辑;
  3. 增加传感器失败告警机制;
  4. 加强 SMBus 通信健壮性处理 |
点击此处查看详细分析解答

关于 general_hardware 组件中 GPU 监控线程异常中断问题的分析与定位建议

根据您提供的背景信息和日志内容,该问题是 GPU 温度监控数据未更新,具体表现为:

  • OS 侧 GPU 已升温至 90°C 以上;
  • BMC 侧仍然显示低温(如 40°C);
  • 日志中出现大量 general_hardware 组件下 Lua 协程异常退出记录;
  • tracechip 无 GPU 芯片访问数据,说明获取硬件状态的线程已停止运行。

以下依据所提供的知识库信息,结合问题现象进行综合分析并提出进一步定位建议。


:magnifying_glass_tilted_left: 一、问题现象总结

现象 描述
温度数据停滞 BMC 显示的 GPU 温度不再随实际变化,出现严重偏差
协程异常退出 多个 co[thread: ...] abnormal exit 错误,集中于 app_preloader.lua
错误信息一致 全部报错均为:bad argument #2 to 'format_msg' (number expected, got nil)
线程数量匹配 异常退出线程数 ≈ 7,与失去监控的 GPU 数量(7/8)一致
无 core dump 未生成核心转储文件,难以回溯调用栈
无芯片访问日志 tracechip 无输出,表明监控线程已终止或阻塞

:books: 二、基于上下文的已知信息分析

1. general_hardware 是 GPU 监控的核心组件

从知识库中的“搜索结果”可知:

  • general_hardware 负责 GPU 卡的电源、温度、状态监控
  • 非 NVIDIA GPU 若需带外管理,需修改 general_hardware 组件代码。
  • 新增 NVIDIA GPU 适配只需添加配置文件(如 Tesla_T4.lua)。

这表明 general_hardware 是管理 GPU 状态采集的主要服务,其内部通过 Lua 协程实现定时采集逻辑。


2. 日志打印出错点分析:format_msg 类型错误

报错详情为:

./opt/bmc/libmc/lualib/mc/logging.lua:253: bad argument #2 to 'format_msg' (number expected, got nil)

说明在调用日志格式化函数时,本应传入一个 数字类型的值(如温度、电压等),但实际传入了 nil

推测流程

  1. 某个 GPU 监控协程尝试读取温度传感器数据;
  2. 读取失败导致返回 nil
  3. 在日志记录时未对 nil 做保护处理,直接传入 format_msg
  4. 引发 Lua 类型错误,协程崩溃退出;
  5. 若无异常捕获机制,整个监控任务中断,后续不再更新数据。

:warning: 此为典型的 未做空值防御的日志打印缺陷,常见于异步监控线程。


3. GPU 温度读取路径分析

(1) 温度数据来源接口

  • 实体:bmc.kepler.Systems.Processor.GPU.Status
  • 接口路径:/bmc/kepler/Systems/${SystemId}/Processors/GPU/${Id}
  • 用途:获取 GPU 的 ECC 状态、错误页数、NvLink 状态等
  • 更新周期:通过 smbus_postbox2 秒轮询一次
{
  "entity": "bmc.kepler.Systems.Processor.GPU.Status",
  "description": "...使用 smbus_postbox 每 2 秒获取一次状态数据"
}

(2) 温度数据字段

  • 属性:PrimaryGPUTemperatureCelsius
  • 类型:Double,只读
  • 来源:GPU 芯片内置传感器,经 I²C/SMBus 返回

文档 Chunk “2” 提供完整属性列表:

属性名 类型 描述
PrimaryGPUTemperatureCelsius Double 主处理器温度(摄氏度)

:white_check_mark: 该字段正是我们期望更新的温度值。若采集过程某环节出错(如 SMBus 通信失败),可能导致读取为 nil


4. 与温度读取相关的告警机制

实体 描述
GPU Temperature Reading Failure 当无法读取 GPU 温度时触发的告警
Temperature Fault 温度读取失败事件,由 contbin_H10L5 模块检测
contbin_H10L5 负责进/出风口、PCH、CPU、RAID 卡、PCIe Switch 的温度读取失败检测
contbin_H20L5 明确包含 GPU 卡温度读取失败 检测(序号 9)

:white_check_mark: 系统具备 GPU 温度读取失败的硬件层面检测能力。

但当前并未看到相关告警产生,可能原因:

  • 传感器通信未完全失败,而是返回 0 或旧缓存值;
  • 监控线程已崩溃,未能上报事件;
  • 事件上报路径也依赖于异常捕获机制,崩溃后无法执行。

5. 可能的根本原因推断

可能原因 支持证据
GPU 温度读取返回 nil,日志模块未防呆 日志错误明确指向 format_msg(number expected, got nil)
Lua 协程无异常捕获机制(pcall 缺失) 多个协程同时崩溃,说明无容错处理
SMBus/I²C 通信因高负载失败 OS 进行 GPU 压力测试 → PCIe 链路繁忙 → 带外通信受影响
VRD 或电源波动影响 BMC 通信 高功耗状态下板级电压不稳定,影响传感器通信
协程资源泄露或调度异常 协程频繁退出可能导致调度器压力增大,加剧雪崩效应

:white_check_mark: 三、进一步定位建议

尽管当前没有生成 core dump,但仍可通过以下方式深入排查:

1. 确认 format_msg 的参数来源

检查 general_hardware 中调用日志的地方,尤其是与 GPU 相关的传感器读取后写日志的代码段。

例如,在获取温度后可能有如下伪代码:

local temp = read_gpu_temp(sensor_id)
log.info("GPU%d Temp: %.2f°C", id, temp) -- ❌ 若 temp 为 nil,此处将报错

建议修复方式

local temp = read_gpu_temp(sensor_id) or 0.0
if not temp then
    log.warn("Failed to read GPU%d temperature, got nil", id)
else
    log.info("GPU%d Temp: %.2f°C", id, temp)
end

2. 启用 Lua 的异常捕获(pcallxpcall

确保每个监控协程都包裹在保护模式中,避免因单点错误导致线程退出。

skynet.fork(function()
    while true do
        local ok, err = pcall(collect_gpu_metrics, gpu_id)
        if not ok then
            log.error("GPU %d monitor thread failed: %s", gpu_id, err)
            -- 可重启采集或上报事件
        end
        skynet.sleep(200) -- 每2秒一次
    end
end)

推荐:所有硬件采集线程均应加 pcall 保护。


3. 检查 smbus_postbox 通信是否超时或失败

使用命令行工具手动测试:

# 查看 smbus 是否能正常读取 GPU 设备(假设地址为0x30)
i2cget -f -y <bus_num> 0x30

或在 Lua 脚本中插入调试日志:

local data = smbus_read(addr)
if not data then
    log.warn("SMBus read failed for GPU at addr 0x%X", addr)
    return nil
end

4. 增加传感器状态监控逻辑

参考 Scanner_GPUTemp.Status 字段设计:

  • 0: 正常
  • 1: 异常(读取失败)

建议为每个 GPU 创建一个状态标记,在读取失败时持续上报“Sensor Failed”事件,并触发 GPU Temperature Reading Failure 告警。


5. 验证高负载下的 I²C 总线稳定性

  • 使用 i2cdetect 检查总线设备是否存在;
  • 在压力测试期间抓取 i2c-bus 使用情况;
  • 检查是否存在总线锁死、ACK 失败等情况。

6. 检查是否触发 Hot Plug Anomaly

虽然本例是 OS 内部压力测试,但极端情况下 GPU 运行状态剧烈变化,可能导致 BMC 认为设备“热插拔”,进入异常状态机。

查看是否有如下现象:

  • GPU Slot 状态频繁切换;
  • Presence 信号抖动;
  • BMC 尝试重新加载驱动失败。

:hammer_and_wrench: 四、临时规避措施

  1. 重启 BMC 或刷写修复版本
    (用户已在日志中尝试此方法,确认有效)

  2. 降低 GPU 压力测试负载
    验证是否为高功耗引发的通信异常边界问题

  3. 限制每个 GPU 监控线程独立运行
    防止某一 GPU 异常影响其他 GPU 的监控


:puzzle_piece: 五、结论与总结

项目 分析结果
根本原因 极可能是 GPU 温度读取返回 nil,而日志模块未做防空处理,导致 format_msg 报错,协程异常退出
传播机制 多个 GPU 监控线程同时失败 → 所有温度更新中断
触发条件 OS 层 GPU 高负载 → 可能引起 SMBus 通信异常 → 读取失败 → 返回 nil
责任组件 general_hardware 中 GPU 监控逻辑缺乏异常容忍机制
解决方案方向
  1. 所有日志参数增加 nil 判断;
  2. 使用 pcall 包裹采集逻辑;
  3. 增加传感器失败告警机制;
  4. 加强 SMBus 通信健壮性处理 |

:pushpin: 六、后续建议

  1. :white_check_mark: 提交 Issue 至 general_hardware 组件维护团队

    • 标题:[Bug] GPU monitor thread crash due to nil value in logging
    • 内容:附当前日志、线程数、现象描述,建议加入 pcall 和防 nil 处理
  2. :white_check_mark: 建议增加运行时健康检查

    • 定期扫描 GPU 监控协程是否存活;
    • 若发现缺失,自动重启采集任务。
  3. :white_check_mark: 推动标准化采集框架建设

    • 将传感器采集、异常处理、日志输出抽象为通用库函数,避免重复错误。

:light_bulb: 最后提示:此问题虽表象为“温度不更新”,实则暴露了 BMC 软件在 异常处理机制上的薄弱环节,建议作为典型案例纳入 DFX(Design for X)改进项,提升系统鲁棒性。

可以使用这个命令来看compute组件的日志,看看是否有报错
mdbctl
attach general_hardware
dloglevel debug
dlogtype local

因为是客户环境,且该问题是偶现问题,客户持续压测大概一周多两周能复现一次,我们没有办法直接上去调试。

从log看似乎是log:xxx接口入参为nil导致的?
我们在排查是不是有些异常情况没有处理好导致传入了nil值。

是否能提供下完整的日志?仅从截图的部分难以分析报错的点大致是在哪块代码附近

感谢支持,一键收集如下:
dump_info.zip (6.8 MB)