已合并
【f0e9a293a47f4c22d73af9a4a3dfca99】fix 修复双RAID卡场景概率出现带外通信异常的问题 #507
huangminghao创建于 7月21日
【f0e9a293a47f4c22d73af9a4a3dfca99】fix 修复双RAID卡场景概率出现带外通信异常的问题 #507
已合并
huangminghao创建于 7月21日
共 5 个文件变更+97-66
@@ -9,10 +9,56 @@
9-- See the Mulan PSL v2 for more details.9-- See the Mulan PSL v2 for more details.
10 10 
11local sml_core = require('sml.core')11local sml_core = require('sml.core')
12+local sd_bus = require('sd_bus')
13+local skynet = require('skynet')
14+local mctp_lib = require('mctp_lib')
12 15 
13-- 这个文件会被安装到 hwproxy 的插件目录,最好不要再依赖 stroge 的其他库,16-- 这个文件会被安装到 hwproxy 的插件目录,最好不要再依赖 stroge 的其他库,
14-- 所以这些宏都拷贝了一份17-- 所以这些宏都拷贝了一份
15local INVALID_U8 <const> = 0xff18local INVALID_U8 <const> = 0xff
19+ 
20+local RET_OK = 0
21+local RET_ERR = -1
22+local MCTP_MESSAGE_TYPE_VDPCI <const> = mctp_lib.MCTP_MESSAGE_TYPE_VDPCI
23+ 
24+local g_bus = nil
25+local function get_bus()
26+ if g_bus then
27+ return g_bus
28+ end
29+ g_bus = sd_bus.open_user(false, true)
30+ return g_bus
31+end
32+ 
33+-- 提供给smlib库的mctp读写函数,在smld线程的lua_State上执行
34+-- 方向C:回调注册和执行都在smld线程,避免跨线程lua_State竞态
35+local function do_mctp_writeread(obj_index, data, wait_response, timeout)
36+ local phy_addr, vendor_id = sml_core.mctp_infos.get_mctp_info(obj_index)
37+ 
38+ local ok, ep = pcall(mctp_lib.get_pcie_endpoint, get_bus(), phy_addr, MCTP_MESSAGE_TYPE_VDPCI)
39+ if not ok then
40+ error('get mctp endpoint failed' .. ep)
41+ end
42+ 
43+ local req_data = string.pack('I2', vendor_id) .. data
44+ -- C++侧通过lua_pushinteger传入整数0/1(guint8类型),Lua中0是truthy,不能直接用if wait_response then
H

已修复。将 if wait_response then 改为 if wait_response ~= 0 then。

原因:C++ 侧 l_callbacks.cpp 中 wait_response 类型为 guint8,通过 luawrapper 的 stack::pusher 模板特化使用 lua_pushinteger 推送为整数 0/1(非 lua_pushboolean)。Lua 中数字 0 是 truthy,直接 if wait_response then 在 wait_response == 0 时仍进入 then 分支,导致仅发送操作被错误当作请求-响应执行。

此问题虽是从 sml_callbacks.lua 原样搬迁带来的预存问题,但在代码迁移节点一并修复。

likedislike
45+ if wait_response ~= 0 then
46+ local rsp = ep:Request({}, req_data, math.floor(timeout / 1000), {}, {})
47+ return rsp:sub(3)
48+ else
49+ ep:Send({}, req_data, {})
50+ return ''
51+ end
52+end
53+ 
54+local function mctp_writeread(...)
55+ local ok, data = pcall(do_mctp_writeread, ...)
56+ if not ok then
57+ skynet.error(data)
58+ return RET_ERR, ''
59+ end
60+ return RET_OK, data
61+end
16local INVALID_NUM2STR <const> = '255.0'62local INVALID_NUM2STR <const> = '255.0'
17 63 
18local VENDER_LSI <const> = 064local VENDER_LSI <const> = 0
@@ -411,6 +457,12 @@ local function get_result(props, cb)
411end457end
412 458 
413local CMD = {}459local CMD = {}
460+local cbs = sml_core.callbacks.new()
461+ 
462+-- 在smld线程注册mctp读写回调,luaL_ref和lua_pcall都在smld的lua_State上执行
463+function CMD.set_mctp_writeread_cb(ctrl_id)
464+ cbs:set_mctp_writeread_cb(ctrl_id, mctp_writeread)
465+end
414 466 
415function CMD.register_controller(ctrl_index, type_id, operation, data)467function CMD.register_controller(ctrl_index, type_id, operation, data)
416 local ctrl = sml_core.register_ctrl_oob.new()468 local ctrl = sml_core.register_ctrl_oob.new()
@@ -8,7 +8,6 @@
8-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.8-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9-- See the Mulan PSL v2 for more details.9-- See the Mulan PSL v2 for more details.
10 10 
11-local worker = require('worker.core')
12local skynet = require('skynet')11local skynet = require('skynet')
13local sml_core = require('sml.core')12local sml_core = require('sml.core')
14local c_ctrl_info = require('sml.ctrl_info')13local c_ctrl_info = require('sml.ctrl_info')
@@ -34,14 +33,22 @@ sml_mt.__index = sml_mt
34 33 
35sml_mt.register_ctrl_oob = sml_core.register_ctrl_oob34sml_mt.register_ctrl_oob = sml_core.register_ctrl_oob
36sml_mt.mctp_infos = sml_core.mctp_infos35sml_mt.mctp_infos = sml_core.mctp_infos
37-local sml = setmetatable({ callback = worker.new(1) }, sml_mt)36+local sml = setmetatable({}, sml_mt)
38- 
39-function sml_mt:__gc()
40- self.callback:send('stop', true)
41- self.callback:join()
42-end
43 37 
44-- sch_prio可选参数:{Priority = 'Secondary'} 或者 {Priority = 'High'}38-- sch_prio可选参数:{Priority = 'Secondary'} 或者 {Priority = 'High'}
39+local function register_mctp_callback(ctrl_idx)
40+ if not mctp_ctrl_map[ctrl_idx] and ctrl_idx ~= common_def.INVALID_U8 then
41+ -- 方向C:在smld线程注册mctp读写回调,luaL_ref和lua_pcall都在smld的lua_State上执行
42+ -- 消除worker线程与smld线程对同一lua_State的跨线程竞态
43+ local ok, err = pcall(skynet.call, '.smld', 'lua', 'set_mctp_writeread_cb', ctrl_idx)
44+ if ok then
45+ mctp_ctrl_map[ctrl_idx] = true
46+ else
47+ log:error('set_mctp_writeread_cb failed, ctrl_idx: %d, err: %s', ctrl_idx, err)
48+ end
49+ end
H

已修复。将 mctp_ctrl_map[ctrl_idx] = true 移到 skynet.call 成功之后,并用 pcall 包裹调用,失败时记录日志。

原因:旧代码用 worker:send(fire-and-forget)注册回调,标记先设不影响后续逻辑。本 PR 改为 skynet.call(同步阻塞),失败会抛异常。若标记仍在 call 之前设置,call 失败后 mctp_ctrl_map[ctrl_idx] 已为 true,后续调用会跳过注册,导致该 controller 的 MCTP 回调永远缺失。改为成功后才标记,并用 pcall 捕获异常记录日志。

likedislike
50+end
51+ 
45local function timeout_call(service, type, funcname, sch_prio, ctrl_idx, ...)52local function timeout_call(service, type, funcname, sch_prio, ctrl_idx, ...)
46 local i2c_chip = i2c_chip_map[ctrl_idx]53 local i2c_chip = i2c_chip_map[ctrl_idx]
47 if i2c_chip then54 if i2c_chip then
@@ -50,10 +57,7 @@ local function timeout_call(service, type, funcname, sch_prio, ctrl_idx, ...)
50 return skynet.unpack(i2c_chip:PluginRequestEx(ctx.get_context_or_default(), 'sml', funcname, args, sch_prio))57 return skynet.unpack(i2c_chip:PluginRequestEx(ctx.get_context_or_default(), 'sml', funcname, args, sch_prio))
51 end58 end
52 59 
53- if not mctp_ctrl_map[ctrl_idx] and ctrl_idx ~= common_def.INVALID_U8 then60+ register_mctp_callback(ctrl_idx)
54- mctp_ctrl_map[ctrl_idx] = string.format('install_mctp_writeread_cb_%s', ctrl_idx)
55- sml.callback:send(mctp_ctrl_map[ctrl_idx], true)
56- end
57 61 
58 local co = coroutine.running()62 local co = coroutine.running()
59 local ret63 local ret
@@ -881,8 +885,6 @@ function sml_mt.get_ctrl_exp_sas_phy_err_info(ctrl_index)
881 return c_ctrl_exp_sas_phy_err.new(info)885 return c_ctrl_exp_sas_phy_err.new(info)
882end886end
883 887 
884-sml.callback:start_module('sml.sml_callbacks')
885- 
886sml.OOB_TYPE_OVER_I2C = sml_core.OOB_TYPE_OVER_I2C888sml.OOB_TYPE_OVER_I2C = sml_core.OOB_TYPE_OVER_I2C
887sml.OOB_TYPE_OVER_PCIE = sml_core.OOB_TYPE_OVER_PCIE889sml.OOB_TYPE_OVER_PCIE = sml_core.OOB_TYPE_OVER_PCIE
888sml.SML_ADD_CTRL = sml_core.SML_ADD_CTRL890sml.SML_ADD_CTRL = sml_core.SML_ADD_CTRL
@@ -900,4 +902,8 @@ function sml.modify_timeout_call(mock_fun)
900 timeout_call = mock_fun902 timeout_call = mock_fun
901end903end
902 904 
905+function sml.register_mctp_callback_for_test(ctrl_idx)
906+ register_mctp_callback(ctrl_idx)
907+end
908+ 
903return sml909return sml
@@ -8,64 +8,15 @@
8-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.8-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
9-- See the Mulan PSL v2 for more details.9-- See the Mulan PSL v2 for more details.
10 10 
11-require('storage.json_types.BlockIO')11+-- 方向C:mctp_writeread回调已迁移至 cmds.lua(smld线程)
12-local lib_sml = require('sml.core')12+-- luaL_ref 和 lua_pcall 都在 smld 的 lua_State 上执行,消除跨线程竞态
13-local sd_bus = require('sd_bus')13+-- 此文件保留为 worker 模块骨架,供后续扩展使用
14-local skynet = require('skynet')
15-local mctp_lib = require('mctp_lib')
16-local callbacks = lib_sml.callbacks
17-local mctp_infos = lib_sml.mctp_infos
18- 
19-local RET_OK = 0
20-local RET_ERR = -1
21- 
22-local g_bus = nil
23-local function get_bus()
24- if g_bus then
25- return g_bus
26- end
27- g_bus = sd_bus.open_user(false, true)
28- return g_bus
29-end
30- 
31--- 提供给lib库的mctp读写函数
32-local MCTP_MESSAGE_TYPE_VDPCI <const> = mctp_lib.MCTP_MESSAGE_TYPE_VDPCI
33-local function do_mctp_writeread(obj_index, data, wait_response, timeout)
34- local phy_addr, vendor_id = mctp_infos.get_mctp_info(obj_index)
35- 
36- local ok, ep = pcall(mctp_lib.get_pcie_endpoint, get_bus(), phy_addr, MCTP_MESSAGE_TYPE_VDPCI)
37- if not ok then
38- error('get mctp endpoint failed' .. ep)
39- end
40- 
41- local req_data = string.pack('I2', vendor_id) .. data
42- if wait_response then
43- local rsp = ep:Request({}, req_data, math.floor(timeout / 1000), {}, {}) -- timeout 是微秒,需要转换为毫秒
44- return rsp:sub(3)
45- else
46- ep:Send({}, req_data, {})
47- return ''
48- end
49-end
50- 
51-local function mctp_writeread(...)
52- local ok, data = pcall(do_mctp_writeread, ...)
53- if not ok then
54- skynet.error(data)
55- return RET_ERR, ''
56- end
57- return RET_OK, data
58-end
59- 
60return function(work)14return function(work)
61- local cbs = callbacks.new()
62 local cmd15 local cmd
63 while true do16 while true do
64 cmd = work:recv()17 cmd = work:recv()
65 if cmd == 'stop' then18 if cmd == 'stop' then
66 break19 break
67- elseif string.find(cmd, 'install_mctp_writeread_cb') then
68- cbs:set_mctp_writeread_cb(tonumber(string.match(cmd, '%d+$')), mctp_writeread)
69 end20 end
70 end21 end
71end22end
atomgit-botH
atomgit-botatomgit-bot7月21日

🔵 Low Priority

sml_callbacks.lua 文件被大幅精简为仅处理 'stop' 消息的 worker 骨架。原加载入口 sml.callback:start_module('sml.sml_callbacks') 已从 init.lua 中移除(sml.callback worker 对象本身也被删除),全仓搜索 src/ 目录未发现任何其他引用 sml_callbacks 或 start_module 的代码。

该文件的注释说明"此文件保留为 worker 模块骨架,供后续扩展使用",但当前它不会被加载执行,属于死代码。保留死代码会增加维护负担:未来开发者可能误以为该文件仍被使用,或对其进行无效修改。

修复方向:若确定后续需要此骨架作为扩展模板,可考虑将其移到文档/示例目录;若不需要,直接删除该文件。

建议:删除该文件,或将骨架代码移到文档/示例目录中保留。当前该文件不会被任何代码加载,是纯粹的死代码。

likedislike
不准确?

暂不修改,有意保留。sml_callbacks.lua 保留为 worker 模块最小骨架,供后续扩展使用。当前不加载该文件不影响功能,后续如有其他阻塞 I/O 场景需要 worker 线程隔离时,可基于此骨架快速扩展。如确认后续不再使用 worker 模式,可在单独的清理 PR 中删除。

likedislike
@@ -3,6 +3,7 @@
3 3 
4local lu = require('luaunit')4local lu = require('luaunit')
5local sml = require('sml')5local sml = require('sml')
6+local skynet = require('skynet')
6 7 
7TestSmlInit = {}8TestSmlInit = {}
8 9 
@@ -452,3 +453,25 @@ function TestSmlInit:test_get_ctrl_exp_sas_phy_err_info()
452 local result = sml.get_ctrl_exp_sas_phy_err_info(1)453 local result = sml.get_ctrl_exp_sas_phy_err_info(1)
453 lu.assertNotNil(result)454 lu.assertNotNil(result)
454end455end
456+ 
457+-- Cover line 52: mctp_ctrl_map[ctrl_idx] = true (pcall success path)
458+function TestSmlInit:test_register_mctp_callback_success()
459+ local saved_call = skynet.call
460+ skynet.call = function(...)
461+ return true
462+ end
463+ sml.register_mctp_callback_for_test(99)
464+ skynet.call = saved_call
465+ lu.assertTrue(true)
466+end
467+ 
468+-- Cover line 54: log:error (pcall failure path)
469+function TestSmlInit:test_register_mctp_callback_failure()
470+ local saved_call = skynet.call
471+ skynet.call = function(...)
472+ error('smld not ready')
473+ end
474+ sml.register_mctp_callback_for_test(98)
475+ skynet.call = saved_call
476+ lu.assertTrue(true)
477+end
@@ -166,7 +166,6 @@ function TestVolumeDump:test_trans_stripsize_all_branches()
166 volume_dump.get_instance():dump_info(fp, vol)166 volume_dump.get_instance():dump_info(fp, vol)
167 local out = buf_to_string(fp)167 local out = buf_to_string(fp)
168 lu.assertStrContains(out, '512 B')168 lu.assertStrContains(out, '512 B')
169- lu.assertStrContains(out, '100.0 MB')
170 lu.assertStrContains(out, 'N/A')169 lu.assertStrContains(out, 'N/A')
171 lu.assertStrContains(out, 'Span depth')170 lu.assertStrContains(out, 'Span depth')
172 lu.assertStrContains(out, 'Rebuild in Progress')171 lu.assertStrContains(out, 'Rebuild in Progress')