| @@ -9,10 +9,56 @@ | |||
| 9 | -- See the Mulan PSL v2 for more details. | 9 | -- See the Mulan PSL v2 for more details. |
| 10 | 10 | ||
| 11 | local sml_core = require('sml.core') | 11 | local 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 | -- 所以这些宏都拷贝了一份 |
| 15 | local INVALID_U8 <const> = 0xff | 18 | local 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 | |||
| 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 | ||
| 16 | local INVALID_NUM2STR <const> = '255.0' | 62 | local INVALID_NUM2STR <const> = '255.0' |
| 17 | 63 | ||
| 18 | local VENDER_LSI <const> = 0 | 64 | local VENDER_LSI <const> = 0 |
| @@ -411,6 +457,12 @@ local function get_result(props, cb) | |||
| 411 | end | 457 | end |
| 412 | 458 | ||
| 413 | local CMD = {} | 459 | local 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 | ||
| 415 | function CMD.register_controller(ctrl_index, type_id, operation, data) | 467 | function 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') | ||
| 12 | local skynet = require('skynet') | 11 | local skynet = require('skynet') |
| 13 | local sml_core = require('sml.core') | 12 | local sml_core = require('sml.core') |
| 14 | local c_ctrl_info = require('sml.ctrl_info') | 13 | local c_ctrl_info = require('sml.ctrl_info') |
| @@ -34,14 +33,22 @@ sml_mt.__index = sml_mt | |||
| 34 | 33 | ||
| 35 | sml_mt.register_ctrl_oob = sml_core.register_ctrl_oob | 34 | sml_mt.register_ctrl_oob = sml_core.register_ctrl_oob |
| 36 | sml_mt.mctp_infos = sml_core.mctp_infos | 35 | sml_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 捕获异常记录日志。 ![]() ![]() | |||
| 50 | +end | ||
| 51 | + | ||
| 45 | local function timeout_call(service, type, funcname, sch_prio, ctrl_idx, ...) | 52 | local 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 then | 54 | 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 | end | 58 | end |
| 52 | 59 | ||
| 53 | - if not mctp_ctrl_map[ctrl_idx] and ctrl_idx ~= common_def.INVALID_U8 then | 60 | + 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 ret | 63 | 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) |
| 882 | end | 886 | end |
| 883 | 887 | ||
| 884 | -sml.callback:start_module('sml.sml_callbacks') | ||
| 885 | - | ||
| 886 | sml.OOB_TYPE_OVER_I2C = sml_core.OOB_TYPE_OVER_I2C | 888 | sml.OOB_TYPE_OVER_I2C = sml_core.OOB_TYPE_OVER_I2C |
| 887 | sml.OOB_TYPE_OVER_PCIE = sml_core.OOB_TYPE_OVER_PCIE | 889 | sml.OOB_TYPE_OVER_PCIE = sml_core.OOB_TYPE_OVER_PCIE |
| 888 | sml.SML_ADD_CTRL = sml_core.SML_ADD_CTRL | 890 | sml.SML_ADD_CTRL = sml_core.SML_ADD_CTRL |
| @@ -900,4 +902,8 @@ function sml.modify_timeout_call(mock_fun) | |||
| 900 | timeout_call = mock_fun | 902 | timeout_call = mock_fun |
| 901 | end | 903 | end |
| 902 | 904 | ||
| 905 | +function sml.register_mctp_callback_for_test(ctrl_idx) | ||
| 906 | + register_mctp_callback(ctrl_idx) | ||
| 907 | +end | ||
| 908 | + | ||
| 903 | return sml | 909 | return 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 | - | ||
| 60 | return function(work) | 14 | return function(work) |
| 61 | - local cbs = callbacks.new() | ||
| 62 | local cmd | 15 | local cmd |
| 63 | while true do | 16 | while true do |
| 64 | cmd = work:recv() | 17 | cmd = work:recv() |
| 65 | if cmd == 'stop' then | 18 | if cmd == 'stop' then |
| 66 | break | 19 | 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 | end | 20 | end |
| 70 | end | 21 | end |
| 71 | end | 22 | end |
🔵 Low Priority
该文件的注释说明"此文件保留为 worker 模块骨架,供后续扩展使用",但当前它不会被加载执行,属于死代码。保留死代码会增加维护负担:未来开发者可能误以为该文件仍被使用,或对其进行无效修改。 修复方向:若确定后续需要此骨架作为扩展模板,可考虑将其移到文档/示例目录;若不需要,直接删除该文件。 建议:删除该文件,或将骨架代码移到文档/示例目录中保留。当前该文件不会被任何代码加载,是纯粹的死代码。 ![]() ![]() 不准确? 暂不修改,有意保留。sml_callbacks.lua 保留为 worker 模块最小骨架,供后续扩展使用。当前不加载该文件不影响功能,后续如有其他阻塞 I/O 场景需要 worker 线程隔离时,可基于此骨架快速扩展。如确认后续不再使用 worker 模式,可在单独的清理 PR 中删除。 ![]() ![]() | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | local lu = require('luaunit') | 4 | local lu = require('luaunit') |
| 5 | local sml = require('sml') | 5 | local sml = require('sml') |
| 6 | +local skynet = require('skynet') | ||
| 6 | 7 | ||
| 7 | TestSmlInit = {} | 8 | TestSmlInit = {} |
| 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) |
| 454 | end | 455 | end |
| 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') |


已修复。将 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 原样搬迁带来的预存问题,但在代码迁移节点一并修复。