Open
feat: 补齐Control查询与稳定设备身份 #16
feat: 补齐Control查询与稳定设备身份 #16
Open
liuxingsenAdd on 13 days ago
Total: 5 files changed+407-18
@@ -1,5 +1,8 @@
1# Changelog1# Changelog
2All notable changes to this project will be documented in this file.2All notable changes to this project will be documented in this file.
3+## [1.130.21] - 2026-10-27
4+修复Control查询回包和Set EID语义,补充稳定设备UUID及按实际注册状态返回的能力查询。
5+ 
3## [1.130.15] - 2026-08-256## [1.130.15] - 2026-08-25
4更新版本号,触发流水线推包7更新版本号,触发流水线推包
5 8 
@@ -2,7 +2,7 @@
2 "name": "mctpd",2 "name": "mctpd",
3 "type": "application",3 "type": "application",
4 "deployConfig": "framework.service",4 "deployConfig": "framework.service",
5- "version": "1.130.15",5+ "version": "1.130.21",
6 "author": "Huawei",6 "author": "Huawei",
7 "license": "Mulan PSL v2",7 "license": "Mulan PSL v2",
8 "options": {8 "options": {
@@ -15,12 +15,14 @@ local c_tasks = require('mc.orm.tasks')
15local signal = require('mc.signal')15local signal = require('mc.signal')
16local network = require('network.core')16local network = require('network.core')
17local mctp_def = require('mctp_def')17local mctp_def = require('mctp_def')
18+local context = require('mc.context')
19+local vos = require('utils.vos')
18 20 
19local MCTP_CMD_SET_ENDPOINT_ID <const> = 0x0121local MCTP_CMD_SET_ENDPOINT_ID <const> = 0x01
20local MCTP_CMD_GET_ENDPOINT_ID <const> = 0x0222local MCTP_CMD_GET_ENDPOINT_ID <const> = 0x02
21--- local MCTP_CMD_GET_ENDPOINT_UUID <const> = 0x0323+local MCTP_CMD_GET_ENDPOINT_UUID <const> = 0x03
22--- local MCTP_CMD_GET_MCTP_VERSION_SUPPORT <const> = 0x0424+local MCTP_CMD_GET_MCTP_VERSION_SUPPORT <const> = 0x04
23--- local MCTP_CMD_GET_MESSAGE_TYPE_SUPPORT <const> = 0x0525+local MCTP_CMD_GET_MESSAGE_TYPE_SUPPORT <const> = 0x05
24-- local MCTP_CMD_GET_VENDOR_DEFINED_MESSAGE_SUPPORT <const> = 0x0626-- local MCTP_CMD_GET_VENDOR_DEFINED_MESSAGE_SUPPORT <const> = 0x06
25-- local MCTP_CMD_RESOLVE_ENDPOINT_ID <const> = 0x0727-- local MCTP_CMD_RESOLVE_ENDPOINT_ID <const> = 0x07
26-- local MCTP_CMD_ALLOCATE_ENDPOINT_IDS <const> = 0x0828-- local MCTP_CMD_ALLOCATE_ENDPOINT_IDS <const> = 0x08
@@ -36,9 +38,9 @@ local MCTP_CMD_DISCOVERY_NOTIFY <const> = 0x0D
36local MCTP_CTRL_CC_SUCCESS <const> = 0x0038local MCTP_CTRL_CC_SUCCESS <const> = 0x00
37-- local MCTP_CTRL_CC_ERROR <const> = 0x0139-- local MCTP_CTRL_CC_ERROR <const> = 0x01
38local MCTP_CTRL_CC_ERROR_INVALID_DATA <const> = 0x0240local MCTP_CTRL_CC_ERROR_INVALID_DATA <const> = 0x02
39--- local MCTP_CTRL_CC_ERROR_INVALID_LENGTH <const> = 0x0341+local MCTP_CTRL_CC_ERROR_INVALID_LENGTH <const> = 0x03
40--- local MCTP_CTRL_CC_ERROR_NOT_READY <const> = 0x0442+local MCTP_CTRL_CC_ERROR_NOT_READY <const> = 0x04
41--- local MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD <const> = 0x0543+local MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD <const> = 0x05
42 44 
43local MCTP_DYNAMIC_EID <const> = 045local MCTP_DYNAMIC_EID <const> = 0
44-- local MCTP_STATIC_EID <const> = 146-- local MCTP_STATIC_EID <const> = 1
@@ -46,7 +48,6 @@ local MCTP_DYNAMIC_EID <const> = 0
46local ROUTE_TO_ROOT <const> = 0x0048local ROUTE_TO_ROOT <const> = 0x00
47local ROUTE_BY_ID <const> = 0x0249local ROUTE_BY_ID <const> = 0x02
48 50 
49-local MCTP_EID_NULL <const> = 0
50local MCTP_EID_BROADCAST <const> = 0xFF51local MCTP_EID_BROADCAST <const> = 0xFF
51 52 
52local MCTP_REQUEST_DEFAULT_TIMEOUT = 15000 -- 默认超时时间 15 秒53local MCTP_REQUEST_DEFAULT_TIMEOUT = 15000 -- 默认超时时间 15 秒
@@ -78,6 +79,17 @@ function c_mctp_discovery:init()
78 end79 end
79 -- 启动set eid失败轮询任务,周期性重发失败ep列表中的设置eid报文80 -- 启动set eid失败轮询任务,周期性重发失败ep列表中的设置eid报文
80 self:start_smbus_eid_poll_task()81 self:start_smbus_eid_poll_task()
82+ if self.mctp_engine.bus then
83+ self.identity_task = c_tasks
84+ .get_instance()
85+ :new_task('mctp_identity' .. self.system_id)
86+ :loop(function(task)
87+ if self.endpoint_uuid or self:load_endpoint_identity() then
88+ task:stop()
89+ end
90+ end)
91+ :set_timeout_ms(1000)
92+ end
atomgit-bot
atomgit-botatomgit-bot13 days ago

🔵 Low Priority

新增的 init 分支(mctp_discovery.lua:82-92)用 c_tasks.get_instance():new_task('mctp_identity'..system_id):loop(...) 注册每 1000ms 执行一次的后台任务,但循环体只有 if not self.endpoint_uuid then self:load_endpoint_identity() end,成功后没有任何 :stop()/返回终止逻辑,self.identity_task 也从未在其它地方被引用(全仓仅此一处赋值)。

证据链:同类任务 start_update_route_table/start_smbus_eid_poll_task 均有显式 stop 入口(stop_update_route_table 等),而身份任务没有;且 update_os_power_status(false) → set_discovered(false) → on_discovery_changed 只做 mctp_engine:reset() 与 stop_update_route_table(),不会停止身份任务。

失败模式:身份一旦加载成功,该任务仍按 1Hz 永久唤醒(每个 system 一个),空转消耗 skynet 定时器与调度;系统下电/复位也不会停止,属于长期存在的后台空转任务。功能上不致命,但属于本次新增的可靠性/资源浪费点。

建议:在循环体内于身份加载成功后停止任务,例如 if self.endpoint_uuid then self.identity_task:stop(); return end,并在 update_os_power_status/mctp_engine:reset() 路径中一并停止或重启该任务,避免永久空转。

likedislike
Incorrect?
liuxingsen
13 days ago Comments:
81 93 
82 self.on_discovery_changed:on(function(status)94 self.on_discovery_changed:on(function(status)
83 if status then95 if status then
@@ -89,6 +101,100 @@ function c_mctp_discovery:init()
89 end)101 end)
90end102end
91 103 
104+function c_mctp_discovery:set_endpoint_identity(identity)
105+ if type(identity) ~= 'string' or (#identity ~= 32 and #identity ~= 36) then
106+ return false
107+ end
108+ local name = identity:gsub('-', ''):lower()
109+ if #name ~= 32 or name:find('[^%x]') or name == string.rep('0', 32) or name == string.rep('f', 32) then
110+ return false
111+ end
112+ -- 用掉电持久化设备UUID派生RFC 4122 v5;不使用可改的SystemGUID或动态EID。
113+ local dns_namespace = '\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8'
114+ local digest = vos.compute_checksum(vos.G_CHECKSUM_SHA1, dns_namespace .. 'openubmc:mctp:' .. name)
115+ local bytes = {}
116+ for i = 1, 16 do
117+ bytes[i] = tonumber(digest:sub(i * 2 - 1, i * 2), 16)
118+ end
119+ bytes[7], bytes[9] = (bytes[7] & 0x0f) | 0x50, (bytes[9] & 0x3f) | 0x80
120+ local uuid = string.char(table.unpack(bytes))
121+ if self.endpoint_uuid and self.endpoint_uuid ~= uuid then
122+ return false
123+ end
124+ self.endpoint_uuid = uuid
125+ return true
126+end
127+ 
128+function c_mctp_discovery:load_endpoint_identity()
129+ if self.endpoint_uuid then
130+ return true
131+ end
132+ local stage = 'GetSubObjects'
133+ local ok, loaded = pcall(function()
134+ local bus, iface = self.mctp_engine.bus, 'bmc.kepler.Assembly'
135+ local objects = bus:timeout_call(
136+ 100,
137+ 'bmc.kepler.maca',
138+ '/bmc/kepler/MdbService',
139+ 'bmc.kepler.Mdb',
140+ 'GetSubObjects',
141+ 'a{ss}sias',
142+ context.new(),
143+ '/bmc/kepler/Managers',
144+ 0,
145+ { iface }
146+ )
147+ stage = 'unique Assembly owner'
148+ local path, owners = next(objects)
149+ assert(path and not next(objects, path), 'missing or ambiguous device identity')
150+ local service = next(owners)
151+ assert(service and not next(owners, service), 'ambiguous device identity owner')
152+ stage = 'GetAllWithContext'
153+ local values = bus.bus
154+ :timeout_call(
155+ 100,
156+ service,
157+ path,
158+ 'bmc.kepler.Object.Properties',
159+ 'GetAllWithContext',
160+ 'a{ss}s',
161+ context.new(),
162+ iface
163+ )
164+ :read()
165+ stage = 'persistent UUID format'
166+ return self:set_endpoint_identity(values.UUID and values.UUID:value())
167+ end)
168+ if (not ok or not loaded) and not self.identity_warned then
169+ self.identity_warned = true
170+ log:notice('[System%s]MCTP endpoint identity is not ready at %s; retrying', self.system_id, stage)
171+ end
172+ return ok and loaded == true
atomgit-bot
atomgit-botatomgit-bot13 days ago

🔵 Low Priority

新增的 load_endpoint_identity(mctp_discovery.lua:132-168)用 local ok, loaded = pcall(...) 包裹整段 DBus 取 UUID 逻辑,但失败时把 pcall 返回的错误对象完全丢弃:日志固定为 '[System%s]MCTP endpoint identity is not ready' 且级别是 log:debug,并用 identity_warned 只打印一次。

证据链:该函数是 endpoint_uuid 的唯一生产来源(Get Endpoint UUID 直接依赖它,未就绪即回 NOT_READY),而其中包含两套不同调用面——先是 bus:timeout_call(...) 调 bmc.kepler.Mdb.GetSubObjects,随后是 bus.bus:timeout_call(...) 调 GetAllWithContext(同一对象两种访问路径)。任一处签名/字段名(如 bus.bus、GetSubObjects 的 a{ss}sias 入参、values.UUID:value())与实际框架不符,都会被 pcall 静默吞掉。

失败模式:只要该调用面与真实框架有偏差,Get Endpoint UUID 将永久返回 NOT_READY,而现场只有一条 debug 级、无错误详情的日志,无法定位是对象不存在、存在歧义还是 API 不匹配;默认日志级别下等同于完全静默。

建议:把 pcall 的第二个返回值保存下来(local ok, err = pcall(...)),首次失败时用 info/warn 级别记录 err(后续重试仍可保持仅一次),便于现场判断是对象缺失/多实例还是 DBus API 不匹配。

likedislike
Incorrect?
liuxingsen
13 days ago Comments:
173+end
174+ 
175+function c_mctp_discovery:has_pldm_responder()
176+ -- 注册表由独立PLDM responder提供;未安装/启用该组件时不宣告PLDM。
177+ local responder = self.mctp_engine.pldm_responder
178+ return responder ~= nil and type(responder.reg_count) == 'number' and responder.reg_count > 0
179+end
180+ 
181+function c_mctp_discovery:version_response(req)
182+ local typ = req.req_data:byte(1)
183+ local versions
184+ -- DSP0236 1.3.1的12.6.2/12.6.3修正1.3.0排版错误;版本大端、从旧到新。
185+ local compatible = '\xf1\xf0\xff\0\xf1\xf1\xff\0\xf1\xf2\xff\0'
186+ if typ == 0 then
187+ versions = compatible
188+ elseif typ == 255 then
189+ versions = compatible .. '\xf1\xf3\xf1\0'
190+ elseif typ == 1 and self:has_pldm_responder() then
191+ versions = '\xf1\xf0\xf0\0' -- DSP0241绑定版本,不是PLDM Base/BIOS版本。
192+ end
193+ local data = versions and string.char(#versions // 4) .. versions or ''
194+ self.ep:response(req, versions and MCTP_CTRL_CC_SUCCESS or 0x80, ROUTE_BY_ID, data)
195+ return true
196+end
197+ 
92function c_mctp_discovery:update_os_power_status(is_power_on)198function c_mctp_discovery:update_os_power_status(is_power_on)
93 self.power_on = is_power_on199 self.power_on = is_power_on
94 if is_power_on then200 if is_power_on then
@@ -105,8 +211,13 @@ function c_mctp_discovery:set_discovered(status)
105 end211 end
106end212end
107 213 
214+local REQUEST_LENGTH = { [1] = 2, [2] = 0, [3] = 0, [4] = 1, [5] = 0, [6] = 1, [11] = 0, [12] = 0 }
108function c_mctp_discovery:control_rx_handler(msg)215function c_mctp_discovery:control_rx_handler(msg)
109 local cmd = msg.command_code216 local cmd = msg.command_code
217+ if REQUEST_LENGTH[cmd] and #msg.req_data ~= REQUEST_LENGTH[cmd] then
218+ self.ep:response(msg, MCTP_CTRL_CC_ERROR_INVALID_LENGTH, ROUTE_BY_ID)
219+ return true
220+ end
110 221 
111 if cmd == MCTP_CMD_PREPARE_FOR_ENDPOINT_DISCOVERY then222 if cmd == MCTP_CMD_PREPARE_FOR_ENDPOINT_DISCOVERY then
112 return self:process_prepare_for_ep_discovery_req(msg)223 return self:process_prepare_for_ep_discovery_req(msg)
@@ -116,8 +227,22 @@ function c_mctp_discovery:control_rx_handler(msg)
116 return self:response(msg)227 return self:response(msg)
117 elseif cmd == MCTP_CMD_SET_ENDPOINT_ID then228 elseif cmd == MCTP_CMD_SET_ENDPOINT_ID then
118 return self:process_set_ep_id_req(msg)229 return self:process_set_ep_id_req(msg)
230+ elseif cmd == MCTP_CMD_GET_ENDPOINT_UUID then
231+ self.ep:response(
232+ msg,
233+ self.endpoint_uuid and MCTP_CTRL_CC_SUCCESS or MCTP_CTRL_CC_ERROR_NOT_READY,
234+ ROUTE_BY_ID,
235+ self.endpoint_uuid
236+ )
237+ return true
238+ elseif cmd == MCTP_CMD_GET_MCTP_VERSION_SUPPORT then
239+ return self:version_response(msg)
240+ elseif cmd == MCTP_CMD_GET_MESSAGE_TYPE_SUPPORT then
241+ self.ep:response(msg, MCTP_CTRL_CC_SUCCESS, ROUTE_BY_ID, self:has_pldm_responder() and '\1\1' or '\0')
242+ return true
119 else243 else
120- return false244+ self.ep:response(msg, MCTP_CTRL_CC_ERROR_UNSUPPORTED_CMD, ROUTE_BY_ID)
245+ return true
121 end246 end
122end247end
123 248 
@@ -178,44 +303,53 @@ function c_mctp_discovery:process_ep_discovery_req(req)
178end303end
179 304 
180function c_mctp_discovery:response(req)305function c_mctp_discovery:response(req)
181- self.mctp_engine:response_ctrl_msg(306+ self.ep:response(
182 req,307 req,
183 MCTP_CTRL_CC_SUCCESS,308 MCTP_CTRL_CC_SUCCESS,
184 ROUTE_BY_ID,309 ROUTE_BY_ID,
185- cmds.mctp_ctrl_resp_get_eid.pack({310+ cmds.mctp_ctrl_resp_get_eid:pack({
186 eid = self.mctp_engine.bmc_eid,311 eid = self.mctp_engine.bmc_eid,
187 eid_type = MCTP_DYNAMIC_EID,312 eid_type = MCTP_DYNAMIC_EID,
188 endpoint_type = 0,313 endpoint_type = 0,
189 medium_data = 0,314 medium_data = 0,
190 })315 })
191 )316 )
192- self:set_discovered(true)
193 return true317 return true
194end318end
195 319 
196local OP_SET_EID <const> = 0320local OP_SET_EID <const> = 0
197local OP_FORCE_EID <const> = 1321local OP_FORCE_EID <const> = 1
322+local OP_SET_DISCOVERED <const> = 3
198local SET_EID_ACCEPTED <const> = 0x0323local SET_EID_ACCEPTED <const> = 0x0
199local SET_EID_REJECTED <const> = 0x1324local SET_EID_REJECTED <const> = 0x1
200function c_mctp_discovery:process_set_ep_id_req(req)325function c_mctp_discovery:process_set_ep_id_req(req)
201 local cc = MCTP_CTRL_CC_ERROR_INVALID_DATA326 local cc = MCTP_CTRL_CC_ERROR_INVALID_DATA
202- local rsp = { allocation_status = 0, eid_pool_size = 0, assignment_status = SET_EID_REJECTED }327+ local rsp = {
328+ allocation_status = 0,
329+ eid_pool_size = 0,
330+ assignment_status = SET_EID_REJECTED,
331+ eid_set = self.mctp_engine.bmc_eid,
332+ }
203 333 
204 local set_eid_req = cmds.mctp_ctrl_req_set_eid:unpack(req.req_data)334 local set_eid_req = cmds.mctp_ctrl_req_set_eid:unpack(req.req_data)
205 if not set_eid_req then335 if not set_eid_req then
206- rsp.eid_set = 0
207 log:error('[System%s]mctp_discovery: unpack set endpoint id data failed', self.system_id)336 log:error('[System%s]mctp_discovery: unpack set endpoint id data failed', self.system_id)
208 else337 else
209 local eid = set_eid_req.eid338 local eid = set_eid_req.eid
210 local op = set_eid_req.operation339 local op = set_eid_req.operation
211- if eid == MCTP_EID_BROADCAST or eid == MCTP_EID_NULL then340+ if op == OP_SET_DISCOVERED then
212- rsp.eid_set = self.mctp_engine.bmc_eid341+ -- PCIe的Set Discovered Flag不改变EID,忽略请求中的EID值。
342+ cc, rsp.assignment_status = MCTP_CTRL_CC_SUCCESS, SET_EID_ACCEPTED
343+ self:set_discovered(true)
344+ elseif eid < 8 or eid == MCTP_EID_BROADCAST then
213 log:error('[System%s]mctp_discovery: set endpoint id failed, invalid eid %d', self.system_id, eid)345 log:error('[System%s]mctp_discovery: set endpoint id failed, invalid eid %d', self.system_id, eid)
214 elseif op == OP_SET_EID or op == OP_FORCE_EID then346 elseif op == OP_SET_EID or op == OP_FORCE_EID then
215 rsp.eid_set = eid347 rsp.eid_set = eid
216 cc = MCTP_CTRL_CC_SUCCESS348 cc = MCTP_CTRL_CC_SUCCESS
217 rsp.assignment_status = SET_EID_ACCEPTED349 rsp.assignment_status = SET_EID_ACCEPTED
218- self.mctp_engine:set_bmc_eid(eid, self.ep.pcie_vdm_ctx.bmc_phy_addr)350+ local transport, physical = self.ep:get_ctx_by_medium(self.ep.medium_type)
351+ self.mctp_engine:set_owner_eid(transport.src, physical.remote_phy_addr)
352+ self.mctp_engine:set_bmc_eid(eid, physical.bmc_phy_addr)
219 log:debug(353 log:debug(
220 '[System%s]mctp_discovery: set bmc_eid=%d, bmc_phy_addr=%d',354 '[System%s]mctp_discovery: set bmc_eid=%d, bmc_phy_addr=%d',
221 self.system_id,355 self.system_id,
@@ -55,8 +55,9 @@ function c_ep_mctp_ctrl:response(req, cc, routing, data)
55 self.mctp_engine:pcie_binding_params_set()55 self.mctp_engine:pcie_binding_params_set()
56 local mctp_ctx, ctx = self:get_ctx_by_medium(self.medium_type)56 local mctp_ctx, ctx = self:get_ctx_by_medium(self.medium_type)
57 mctp_ctx.tag_owner = false57 mctp_ctx.tag_owner = false
58+ -- 查询也可来自其他端点,响应沿本次请求返回,不改成Bus Owner地址。
59+ mctp_ctx.dest = mctp_ctx.src
58 mctp_ctx.src = self.mctp_engine.bmc_eid60 mctp_ctx.src = self.mctp_engine.bmc_eid
59- mctp_ctx.dest = self.mctp_engine.owner_eid
60 61 
61 if ctx == self.pcie_vdm_ctx then62 if ctx == self.pcie_vdm_ctx then
62 self.pcie_vdm_ctx.routing = routing63 self.pcie_vdm_ctx.routing = routing
@@ -67,6 +68,9 @@ function c_ep_mctp_ctrl:response(req, cc, routing, data)
67end68end
68 69 
69function c_ep_mctp_ctrl:process(_, msg)70function c_ep_mctp_ctrl:process(_, msg)
71+ if msg.datagram or msg.rq ~= self.mctp_ctx.tag_owner then
72+ return true
73+ end
70 local cmd = msg.command_code74 local cmd = msg.command_code
71 if msg.rq == false then75 if msg.rq == false then
72 if not self.mctp_engine:wakeup_request(self:make_rsp_id(msg.instance_id), msg) then76 if not self.mctp_engine:wakeup_request(self:make_rsp_id(msg.instance_id), msg) then
@@ -33,6 +33,252 @@ local function make_fake_task()
33 return task33 return task
34end34end
35 35 
36+TestMctpControlAdmission = {}
37+ 
38+function TestMctpControlAdmission:setup()
39+ local old = c_tasks.get_instance
40+ c_tasks.get_instance = function()
41+ return {
42+ new_task = function()
43+ return make_fake_task()
44+ end,
45+ }
46+ end
47+ local ok, err = pcall(function()
48+ self.engine = c_mctp_engine.new(nil, 1)
49+ self.discovery = c_mctp_discovery.new(1, self.engine)
50+ end)
51+ c_tasks.get_instance = old
52+ lu.assertTrue(ok, tostring(err))
53+ self.engine:set_bmc_eid(11, 0x300)
54+ self.engine:set_owner_eid(9, 0x2000)
55+ self.discovery.power_on = true
56+ self.discovery.start_update_route_table = function() end
57+ self.frames = {}
58+ self.engine.hw_pcie_proto:set_tx_handler(function()
59+ return true
60+ end)
61+ self.engine.pcie_proto:set_capture_handler(function(outgoing, _, frame)
62+ if outgoing then
63+ self.frames[#self.frames + 1] = frame
64+ end
65+ end)
66+end
67+ 
68+function TestMctpControlAdmission:request(command, data, source, flags, control_flags)
69+ local physical = require('mctp').pcie_vdm_ctx.new()
70+ physical.routing, physical.remote_phy_addr, physical.bmc_phy_addr = 2, 0x2100, 0x300
71+ local packet = string.char(1, 11, source or 10, flags or 0xc9, 0, control_flags or 0x87, command) .. (data or '')
72+ local ok, err = pcall(self.engine.mctp_proto.pop, self.engine.mctp_proto, packet, physical, mctp_def.PHY_MEDIUM_PCI)
73+ lu.assertTrue(ok, 'Control dispatch raised: ' .. tostring(err))
74+end
75+ 
76+function TestMctpControlAdmission:test_get_eid_uses_request_route_without_discovering()
77+ self:request(2)
78+ lu.assertEquals(#self.frames, 1)
79+ local frame = self.frames[1]
80+ lu.assertEquals(frame:sub(9, 10), '\x21\x00')
81+ lu.assertEquals(frame:sub(13, 23), '\x01\x0a\x0b\xc1\x00\x07\x02\x00\x0b\x00\x00')
82+ lu.assertFalse(self.discovery.discovered)
83+ lu.assertEquals({ self.engine.owner_eid, self.engine.owner_phy_addr }, { 9, 0x2000 })
84+end
85+ 
86+function TestMctpControlAdmission:test_bad_length_replies_without_state_change()
87+ for _, command in ipairs({ 1, 2, 3, 4, 5, 6, 11, 12 }) do
88+ self:request(command, 'unexpected')
89+ local frame = self.frames[#self.frames]
90+ lu.assertNotNil(frame, 'no error response for command ' .. command)
91+ lu.assertEquals(frame:byte(20), 3)
92+ lu.assertFalse(self.discovery.discovered)
93+ lu.assertEquals(self.engine.bmc_eid, 11)
94+ end
95+end
96+ 
97+function TestMctpControlAdmission:test_datagram_and_wrong_tag_owner_have_no_response()
98+ self:request(1, '\0\12', 9, 0xc9, 0xc7)
99+ self:request(1, '\0\12', 9, 0xc1, 0x87)
100+ lu.assertEquals(self.frames, {})
101+ lu.assertEquals(self.engine.bmc_eid, 11)
102+ lu.assertFalse(self.discovery.discovered)
103+end
104+ 
105+function TestMctpControlAdmission:test_set_eid_rejects_reserved_ids_and_invalid_operations()
106+ for _, eid in ipairs({ 0, 1, 2, 3, 4, 5, 6, 7, 255 }) do
107+ self:request(1, string.char(0, eid), 9)
108+ lu.assertEquals(self.frames[#self.frames]:byte(20), 2)
109+ lu.assertEquals(self.engine.bmc_eid, 11)
110+ lu.assertFalse(self.discovery.discovered)
111+ end
112+ self:request(1, '\x02\x0c', 9)
113+ lu.assertEquals(self.frames[#self.frames]:byte(20), 2)
114+ lu.assertEquals(self.engine.bmc_eid, 11)
115+end
116+ 
117+function TestMctpControlAdmission:test_set_discovered_flag_preserves_eid()
118+ for _, ignored_eid in ipairs({ 0, 255, 12 }) do
119+ self:request(1, string.char(3, ignored_eid), 9)
120+ lu.assertEquals(self.frames[#self.frames]:sub(20, 23), '\x00\x00\x0b\x00')
121+ lu.assertTrue(self.discovery.discovered)
122+ lu.assertEquals(self.engine.bmc_eid, 11)
123+ end
124+end
125+ 
126+function TestMctpControlAdmission:test_set_eid_captures_request_owner_and_replies_with_new_address()
127+ self:request(1, '\0\12', 9)
128+ lu.assertEquals(self.frames[#self.frames]:sub(13, 23), '\x01\x09\x0c\xc1\x00\x07\x01\x00\x00\x0c\x00')
129+ lu.assertTrue(self.discovery.discovered)
130+ lu.assertEquals({ self.engine.bmc_eid, self.engine.bmc_phy_addr }, { 12, 0x300 })
131+ lu.assertEquals({ self.engine.owner_eid, self.engine.owner_phy_addr }, { 9, 0x2100 })
132+end
133+ 
134+function TestMctpControlAdmission:test_uuid_is_stable_derived_identity_and_not_eid()
135+ self:request(3)
136+ lu.assertEquals(self.frames[#self.frames]:byte(20), 4)
137+ lu.assertTrue(self.discovery:set_endpoint_identity('012B0242-2022-A6E3-11EE-006322669F60'))
138+ self:request(3)
139+ local expected = '\xe0\x10\xd7\x9d\x75\xda\x5d\x5b\x9a\xfe\xf5\x6b\x26\x2f\x8c\x36'
140+ lu.assertEquals(self.frames[#self.frames]:sub(20, 36), '\0' .. expected)
141+ lu.assertFalse(self.discovery:set_endpoint_identity('00000000-0000-0000-0000-000000000000'))
142+ self:request(1, '\0\12', 9)
143+ self.engine:set_bmc_eid(11, 0x300)
144+ self:request(3)
145+ lu.assertEquals(self.frames[#self.frames]:sub(21, 36), expected)
146+ lu.assertTrue(self.discovery:set_endpoint_identity('012b02422022a6e311ee006322669f60'))
147+ lu.assertEquals(self.discovery.endpoint_uuid, expected)
148+end
149+ 
150+function TestMctpControlAdmission:test_version_type_and_vendor_queries_reflect_responder_registration()
151+ -- DSP0236 1.3.1勘误:Control与Base版本列表不同,保留兼容版本。
152+ self:request(4, '\0')
153+ lu.assertEquals(self.frames[#self.frames]:sub(20, 33), '\0\3\xf1\xf0\xff\0\xf1\xf1\xff\0\xf1\xf2\xff\0')
154+ self:request(4, '\xff')
155+ lu.assertEquals(
156+ self.frames[#self.frames]:sub(20, 37),
157+ '\0\4\xf1\xf0\xff\0\xf1\xf1\xff\0\xf1\xf2\xff\0\xf1\xf3\xf1\0'
158+ )
159+ self:request(5)
160+ lu.assertEquals(self.frames[#self.frames]:sub(20, 21), '\0\0')
161+ self:request(4, '\1')
162+ lu.assertEquals(self.frames[#self.frames]:byte(20), 0x80)
163+ self.engine.pldm_responder = { reg_count = 1 }
164+ self:request(5)
165+ lu.assertEquals(self.frames[#self.frames]:sub(20, 22), '\0\1\1')
166+ self:request(4, '\1')
167+ lu.assertEquals(self.frames[#self.frames]:sub(20, 25), '\0\1\xf1\xf0\xf0\0')
168+ self.engine.pldm_responder.reg_count = 0
169+ self:request(5)
170+ lu.assertEquals(self.frames[#self.frames]:sub(20, 21), '\0\0')
171+ for _, registry in ipairs({ {}, { reg_count = '1' } }) do
172+ self.engine.pldm_responder = registry
173+ lu.assertFalse(self.discovery:has_pldm_responder())
174+ end
175+ for _, selector in ipairs({ 0, 1, 255 }) do
176+ self:request(6, string.char(selector))
177+ lu.assertEquals(self.frames[#self.frames]:byte(20), 5)
178+ end
179+end
180+ 
181+function TestMctpControlAdmission:test_identity_background_retries_and_stops_after_success()
182+ local previous, tasks = c_tasks.get_instance, {}
183+ c_tasks.get_instance = function()
184+ return {
185+ new_task = function(_, name)
186+ local task = make_fake_task()
187+ function task:loop(cb)
188+ self.callback = cb
189+ return self
190+ end
191+ function task:set_timeout_ms(ms)
192+ self.timeout_ms = ms
193+ return self
194+ end
195+ function task:stop()
196+ self.stopped = true
197+ end
198+ tasks[name] = task
199+ return task
200+ end,
201+ }
202+ end
203+ self.engine.bus = {}
204+ local ok, obj = pcall(c_mctp_discovery.new, 7, self.engine)
205+ c_tasks.get_instance = previous
206+ lu.assertTrue(ok, tostring(obj))
207+ local task = tasks.mctp_identity7
208+ lu.assertNotNil(task)
209+ lu.assertEquals(task.timeout_ms, 1000)
210+ local calls = 0
211+ obj.load_endpoint_identity = function(self)
212+ calls = calls + 1
213+ return calls > 1 and self:set_endpoint_identity('012B0242-2022-A6E3-11EE-006322669F60')
214+ end
215+ task.callback(task)
216+ lu.assertFalse(task.stopped == true)
217+ task.callback(task)
218+ lu.assertNotNil(obj.endpoint_uuid)
219+ lu.assertTrue(task.stopped)
220+ lu.assertEquals(calls, 2)
221+end
222+ 
223+function TestMctpControlAdmission:test_identity_discovery_retries_and_freezes_valid_device_uuid()
224+ local mode, calls = 'missing', {}
225+ self.engine.bus = {
226+ timeout_call = function(_, budget, service, path, interface, method)
227+ calls[#calls + 1] = method
228+ lu.assertEquals(
229+ { budget, service, path, interface, method },
230+ { 100, 'bmc.kepler.maca', '/bmc/kepler/MdbService', 'bmc.kepler.Mdb', 'GetSubObjects' }
231+ )
232+ if mode == 'error' then
233+ error('unavailable')
234+ end
235+ if mode == 'missing' then
236+ return {}
237+ end
238+ if mode == 'ambiguous' then
239+ return { ['/a'] = {}, ['/b'] = {} }
240+ end
241+ return { ['/bmc/kepler/Managers/1/Assembly'] = { ['bmc.kepler.bmc_network'] = {} } }
242+ end,
243+ bus = {
244+ timeout_call = function(_, budget, service, path, interface, method)
245+ calls[#calls + 1] = method
246+ lu.assertEquals({ budget, service, path, interface, method }, {
247+ 100,
248+ 'bmc.kepler.bmc_network',
249+ '/bmc/kepler/Managers/1/Assembly',
250+ 'bmc.kepler.Object.Properties',
251+ 'GetAllWithContext',
252+ })
253+ return {
254+ read = function()
255+ return {
256+ UUID = {
257+ value = function()
258+ return mode == 'invalid' and 'invalid' or '012B0242-2022-A6E3-11EE-006322669F60'
259+ end,
260+ },
261+ }
262+ end,
263+ }
264+ end,
265+ },
266+ }
267+ for _, state in ipairs({ 'missing', 'error', 'ambiguous', 'invalid' }) do
268+ mode = state
269+ lu.assertFalse(self.discovery:load_endpoint_identity())
270+ lu.assertIsNil(self.discovery.endpoint_uuid)
271+ end
272+ mode = 'valid'
273+ lu.assertTrue(self.discovery:load_endpoint_identity())
274+ local uuid, completed_calls = self.discovery.endpoint_uuid, #calls
275+ mode = 'error'
276+ lu.assertTrue(self.discovery:load_endpoint_identity())
277+ lu.assertEquals(#calls, completed_calls)
278+ lu.assertFalse(self.discovery:set_endpoint_identity('112B0242-2022-A6E3-11EE-006322669F60'))
279+ lu.assertEquals(self.discovery.endpoint_uuid, uuid)
280+end
atomgit-bot
atomgit-botatomgit-bot13 days ago

🔵 Low Priority

新增的核心行为"后台有界读取掉电保存的 Assembly.UUID"依赖 init() 中 if self.mctp_engine.bus then ... new_task('mctp_identity'..system_id):loop(...):set_timeout_ms(1000) end 这段接线(mctp_discovery.lua:82-92):它是在生产环境中给 endpoint_uuid 赋值的唯一入口,没有它 Get Endpoint UUID 永远回 NOT_READY。

证据链:新增测试 TestMctpControlAdmission:setup 用 c_mctp_engine.new(nil, 1) 构造(bus == nil),identity 分支被跳过;test_identity_discovery_retries_and_freezes_valid_device_uuid(test/unit/test_discovery.lua:177-234)是在构造之后直接调用 load_endpoint_identity(),并且 make_fake_task 的 loop() 直接返回而不执行循环体(test/unit/test_discovery.lua:26-28)。

失败模式:若 init() 中的任务注册写错(任务名重复、loop 回调签名不匹配、set_timeout_ms 调用顺序变化)或该分支被漏掉,endpoint_uuid 在生产中永远为 nil,而 114 个单测仍全绿,回归完全不可见。PR 自验证报告也承认"后台身份任务的注册/自动重试接线尚待实际部署验证"。

建议:补一个用例:用带 bus 的 engine 构造 discovery(stub c_tasks.get_instance 记录被创建的任务名与 set_timeout_ms 参数,并让 fake task 的 loop 捕获回调),断言 init 后存在名为 mctp_identity<system_id> 的任务、超时 1000ms,且手动执行一次回调后 endpoint_uuid 被写入、再次执行不会再发 DBus 调用。

likedislike
Incorrect?
liuxingsen
13 days ago Comments:
281+ 
36TestMctpDiscovery = {}282TestMctpDiscovery = {}
37 283 
38function TestMctpDiscovery:setupClass()284function TestMctpDiscovery:setupClass()
@@ -114,6 +360,7 @@ function TestMctpDiscovery:test_mctp_discovery_success()
114 end)360 end)
115 361 
116 self.mctp_discovery:smbus_set_eid_to_endpoint(362 self.mctp_discovery:smbus_set_eid_to_endpoint(
363+ 'discovery-success',
117 target_eid,364 target_eid,
118 ref_chip,365 ref_chip,
119 medium_type,366 medium_type,
@@ -158,6 +405,7 @@ function TestMctpDiscovery:test_mctp_discovery_failed()
158 local ref_chip = { path = path }405 local ref_chip = { path = path }
159 406 
160 self.mctp_discovery:smbus_set_eid_to_endpoint(407 self.mctp_discovery:smbus_set_eid_to_endpoint(
408+ 'discovery-failed',
161 target_eid,409 target_eid,
162 ref_chip,410 ref_chip,
163 medium_type,411 medium_type,