已开启
[缺陷] 双SP686C RAID卡MCTP通信失败:worker线程与smld线程跨lua_State竞态 #453
huangminghao创建于  8月7日
huangminghao成员
8月7日 创建

环境信息

  • 产品版本: openUBMC storage 组件(涉及 src/lualib/sml/init.lua、sml_callbacks.lua、include/hwproxy/plugins/sml/cmds.lua)
  • 操作系统: openEuler (BMC ARM64)
  • 硬件平台: 双 SP686C RAID 卡场景
  • 复现概率: 必现(200 次 AC 上下电必现)

问题详细描述

https://discuss.openubmc.cn/t/topic/6643

预置条件

  1. BMC 系统接入双 SP686C RAID 卡
  2. storage 组件通过 SML 库与 RAID 卡进行 MCTP 通信
  3. worker 线程通过 worker.new(1) 创建,加载 sml_callbacks.lua 模块

操作步骤

  1. BMC 上电启动,storage 服务初始化
  2. init.lua 调用 worker.new(1) 创建独立 OS 线程(拥有独立的 lua_State,记为 L_worker)
  3. sml.callback:start_module('sml.sml_callbacks') 在 worker 线程中加载 sml_callbacks.lua
  4. 当首次访问 RAID 卡时,init.lua 通过 sml.callback:send('install_mctp_writeread_cb_X', true) 向 worker 线程发送消息
  5. worker 线程收到消息后调用 cbs:set_mctp_writeread_cb(ctrl_id, mctp_writeread),底层执行 luaL_ref(L_worker) 将回调注册到 L_worker 的 registry
  6. ref_value 机制捕获 m_L = main_thread(L_worker)(即 L_worker 的主线程引用)
  7. smld 线程处理命令(如 register_controller)时,C++ sml_core 库通过 lua_rawgeti(m_L, ref) + lua_pcall(m_L, ...) 调用回调
  8. 上述 lua_pcall 从 smld OS 线程执行,但操作的 lua_State 是 L_worker → 跨线程竞态

结果描述

实际结果: 双 SP686C RAID 卡场景下,MCTP 通信间歇性失败,200 次 AC 上下电必现。worker 线程与 smld 线程同时操作同一个 lua_State(L_worker),导致 Lua 栈/registry 损坏,MCTP 读写回调执行异常。

预期结果: MCTP 通信稳定可靠,多 RAID 卡场景下无间歇性失败。

问题影响

  • 功能不可用: 双 RAID 卡场景下 MCTP 通信失败,RAID 卡管理功能(信息查询、配置等)不可用
  • 严重程度: 阻断(必现,影响核心存储管理功能)

问题定位情况

根因分析

问题根因是 worker 线程与 smld 线程跨 lua_State 竞态:

  1. worker.new(1) 创建独立 OS 线程,拥有独立的 lua_State(L_worker)
  2. sml_callbacks.lua 在 worker 线程中执行 cbs:set_mctp_writeread_cb(ctrl_id, mctp_writeread),底层调用 luaL_ref(L_worker) 将回调注册到 L_worker 的 registry
  3. ref_value 机制捕获 m_L = main_thread(L_worker)(L_worker 的主线程)
  4. 当 smld 线程处理命令时,C++ sml_core 库通过 lua_rawgeti(m_L, ref) + lua_pcall(m_L, ...) 调用回调
  5. 这是从 smld OS 线程操作 L_worker 的 lua_State → 两个线程同时操作同一个 lua_State 的栈和 registry → 栈损坏/崩溃/MCTP 通信失败

关键代码路径:

  • src/lualib/sml/init.lua line 37: local sml = setmetatable({ callback = worker.new(1) }, sml_mt) — 创建 worker 线程
  • src/lualib/sml/init.lua line 53-55: sml.callback:send(mctp_ctrl_map[ctrl_idx], true) — 发送注册消息到 worker 线程
  • src/lualib/sml/sml_callbacks.lua line 67-68: cbs:set_mctp_writeread_cb(tonumber(...), mctp_writeread) — 在 L_worker 上执行 luaL_ref
  • C++ sml_core 库: ref_value 构造时 luaL_ref(L_worker) + 捕获 m_L = main_thread(L_worker),执行时 lua_rawgeti(m_L) + lua_pcall(m_L) — 从 smld 线程操作 L_worker

建议解决方案

将 MCTP 读写回调的注册和执行统一到 smld 线程的 lua_State(L_smld),消除跨线程竞态:

  1. 移除 worker 线程:init.lua 中删除 worker.new(1)、__gc、start_module('sml.sml_callbacks')
  2. 迁移回调代码到 cmds.lua:将 do_mctp_writeread / mctp_writeread 函数从 sml_callbacks.lua 迁移到 include/hwproxy/plugins/sml/cmds.lua(smld 线程代码)
  3. 新增 CMD 命令:在 cmds.lua 中新增 CMD.set_mctp_writeread_cb(ctrl_id),调用 cbs:set_mctp_writeread_cb(ctrl_id, mctp_writeread)
  4. 改用 skynet.call 注册:init.lua 中将 sml.callback:send(...) 改为 skynet.call('.smld', 'lua', 'set_mctp_writeread_cb', ctrl_idx)

修复后效果:

  • luaL_ref 和 lua_pcall 都在 smld 线程的 L_smld 上执行,彻底消除跨线程竞态
  • skynet.call 保证回调注册完成后再执行后续命令(旧代码 worker:send 与 smld 命令处理无顺序保证)
  • 阻塞 I/O(ep:Request)始终在 smld 线程执行(旧设计中也是如此),移除 worker 不改变阻塞行为

关联 PR: https://gitcode.com/openUBMC/storage/pull/507

临时规避方案

无临时规避方案,需通过代码修复消除跨线程竞态。

附件

likedislike
openUBMC-robot
openUBMC-robot成员
8月7日 评论:

注意

此 Issue 需要负责人。 如果没有指派时, WeberChen 是默认的负责人

likedislike
openUBMC-robotopenUBMC-robot成员
8月7日 添加了label:sig/sig-hardware
openUBMC-robotopenUBMC-robot成员
8月7日 将 WeberChen 设为负责人
openUBMC-robot
openUBMC-robot成员
8月7日 评论:

欢迎来到 openUBMC 社区

Hey @yelmh , 感谢你对社区的贡献.

机器人使用手册

有关指令的使用,可以点击 说明文档 或 论坛 查看详情。开发人员可以在每个PR或Issue下方评论特定指令来触发机器人任务。

联系指引

有疑问可以联系 SIG: sig-hardware ,
维护者是: @changxingOpenUBMC, @nathanael, @xiaoxinglxs, @yelvsub ,
优秀贡献者是: @dli_bmc, @weiziang, @yelmh .

likedislike
openUBMC-robot
openUBMC-robot成员
8月7日 评论:

Issue 工作流介绍

社区的需求和缺陷的工作流状态处于纳管中。

类型 上个状态 下个状态 评论命令 谁可以操作
需求 待办的 已接纳 /todo2accepted SIG组: sig-release-management (maintainer)
需求 已接纳 开发中 /accepted2developing 仓库成员角色: Maintainer, Committer, Contributor, Owner
需求 开发中 转测中 /developing2testing 仓库成员角色: Maintainer, Committer, Contributor, Owner
需求 转测中 待验收 /testing2UAT 仓库成员角色: Maintainer, Committer, Contributor, Owner
需求 待验收 已完成 /UAT2done SIG组: sig-QA (maintainer,committer)
需求 待办的 已挂起 /todo2suspended SIG组: sig-release-management (maintainer)
需求 已挂起 已接纳 /suspended2accepted SIG组: sig-release-management (maintainer)
需求 待办的 已拒绝 /todo2rejected SIG组: sig-release-management (maintainer)
需求 已拒绝 已接纳 /rejected2accepted SIG组: sig-release-management (maintainer)
likedislike
Hhuangminghao成员
8月7日 修改了issue 的描述
Hhuangminghao成员
8月7日 关联了pull request:feat: test
Hhuangminghao成员
8月7日 issue类型由 需求 改变为 缺陷
Hhuangminghao成员
8月7日 修改了issue 的描述
Hhuangminghao成员
8月7日 关联了pull request:fix: 修复双RAID卡场景概率出现带外通信异常的问题
huangminghao成员
9月14日 评论:

/todo2fixing

likedislike
openUBMC-robotopenUBMC-robot成员
9月14日 issue状态由 待修复 改变为 修复中
huangminghao成员
9月14日 评论:
/fixing2UAT
likedislike
openUBMC-robotopenUBMC-robot成员
9月14日 issue状态由 修复中 改变为 待验收
HuaKun_caoluyang成员
18 天前 评论:

200 次 AC 上下电必现么,为什么是200次,好奇怪

likedislike