Merged
[feature] CCU 新增 Variable/Event 资源预约与地址暴露接口 #4317
ting010Add on Jul 31
[feature] CCU 新增 Variable/Event 资源预约与地址暴露接口 #4317
Merged
ting010Add on Jul 31
Total: 18 files changed+1307-4
@@ -25,19 +25,25 @@
25namespace AscendC {25namespace AscendC {
26namespace ccu {26namespace ccu {
27 27 
28-// 主模板未实现:未特化的资源类型实例化 Array<T> 时编译失败,
29-// 当前期次仅 Variable / Event / Buffer 提供底层 CcuBlock*Alloc C 接口。
30template <typename T> struct CcuArrayTraits;28template <typename T> struct CcuArrayTraits;
31 29 
32template <> struct CcuArrayTraits<Variable> {30template <> struct CcuArrayTraits<Variable> {
33 using Handle = CcuVariableHandle;31 using Handle = CcuVariableHandle;
34 static CcuResult BlockAlloc(Handle* h, uint32_t n) { return CcuBlockVariableAlloc(h, n); }32 static CcuResult BlockAlloc(Handle* h, uint32_t n) { return CcuBlockVariableAlloc(h, n); }
33+ static CcuResult CreateByAcquire(Handle acqHandle, uint32_t index, Handle* h)
34+ {
35+ return CcuVariableGetByIndex(acqHandle, index, h);
36+ }
35 static void SetHandle(Variable& v, Handle h) { v.handle = h; }37 static void SetHandle(Variable& v, Handle h) { v.handle = h; }
36};38};
37 39 
38template <> struct CcuArrayTraits<Event> {40template <> struct CcuArrayTraits<Event> {
39 using Handle = CcuEventHandle;41 using Handle = CcuEventHandle;
40 static CcuResult BlockAlloc(Handle* h, uint32_t n) { return CcuBlockEventAlloc(h, n); }42 static CcuResult BlockAlloc(Handle* h, uint32_t n) { return CcuBlockEventAlloc(h, n); }
43+ static CcuResult CreateByAcquire(Handle acqHandle, uint32_t index, Handle* h)
44+ {
45+ return CcuEventGetByIndex(acqHandle, index, h);
46+ }
41 static void SetHandle(Event& e, Handle h) { e.handle = h; }47 static void SetHandle(Event& e, Handle h) { e.handle = h; }
42};48};
43 49 
@@ -47,8 +53,6 @@ template <> struct CcuArrayTraits<CcuBuffer> {
47 static void SetHandle(CcuBuffer& b, Handle h) { b.handle = h; }53 static void SetHandle(CcuBuffer& b, Handle h) { b.handle = h; }
48};54};
49 55 
50-// 连续资源容器:在构造时一次性 BlockAlloc 出 count 个底层句柄并填充给占位元素。
51-// 元素本身通过 NoAllocTag 私有构造跳过单元 Alloc,避免与 BlockAlloc 双重分配。
52template <typename T>56template <typename T>
53class Array final {57class Array final {
54public:58public:
@@ -77,6 +81,34 @@ public:
77 }81 }
78 }82 }
79 83 
84+ Array(typename CcuArrayTraits<T>::Handle acqHandle, uint32_t count) : count_(count) {
85+ if (count == 0) {
86+ return;
87+ }
88+ using H = typename CcuArrayTraits<T>::Handle;
89+ elems_ = static_cast<T*>(::operator new(sizeof(T) * count));
90+ for (uint32_t i = 0; i < count; ++i) {
91+ ::new (static_cast<void*>(&elems_[i])) T(detail::NoAllocTag{});
92+ }
93+ for (uint32_t i = 0; i < count; ++i) {
94+ H handle{};
95+ auto ret = CcuArrayTraits<T>::CreateByAcquire(acqHandle, i, &handle);
96+ if (ret != CcuResult::CCU_SUCCESS) {
97+ std::string errMsg = "Array creation failed at index " + std::to_string(i) +
严正行

[LOW] Array 构造函数错误路径字符串拼接可优化

问题分析:

  • 第 97-99 行:使用 + 操作符拼接字符串,会产生多个临时对象
std::string errMsg = "Array creation failed at index " + std::to_string(i) +
    ", requested count=" + std::to_string(count) +
    "; the acquire handle likely holds fewer resources than count";
  • 这在错误路径(很少发生)上影响很小,但可使用更高效的方式

建议修复: 使用 std::ostringstream 或 fmt::format(如果可用)优化字符串拼接。

说明:这是微优化,在错误路径上影响不大。

include/ccu/ccu_array.hpp:97

likedislike
98+ ", requested count=" + std::to_string(count) +
99+ "; the acquire handle likely holds fewer resources than count";
100+ for (uint32_t j = 0; j < count; ++j) {
101+ elems_[j].~T();
102+ }
103+ ::operator delete(elems_);
104+ elems_ = nullptr;
105+ count_ = 0;
106+ throw ::AscendC::ccu::detail::CcuException(ret, errMsg.c_str());
107+ }
108+ CcuArrayTraits<T>::SetHandle(elems_[i], handle);
109+ }
110+ }
111+ 
80 ~Array() {112 ~Array() {
81 if (elems_ == nullptr) {113 if (elems_ == nullptr) {
82 return;114 return;
@@ -29,6 +29,11 @@ public:
29 CCU_THROW_IF_FAILED(CcuEventAlloc(&this->handle),"CcuEventAlloc: failed"); 29 CCU_THROW_IF_FAILED(CcuEventAlloc(&this->handle),"CcuEventAlloc: failed");
30 }30 }
31 31 
32+ explicit Event(CcuEventHandle acqHandle, uint32_t index = 0) {
33+ CCU_THROW_IF_FAILED(CcuEventGetByIndex(acqHandle, index, &this->handle),
34+ "CcuEventGetByIndex: failed");
35+ }
36+ 
32 Event(const Event& other) : handle(other.handle) {}37 Event(const Event& other) : handle(other.handle) {}
33 38 
34 Event(Event&& other) noexcept : handle(other.handle) {}39 Event(Event&& other) noexcept : handle(other.handle) {}
@@ -18,6 +18,51 @@
18extern "C" {18extern "C" {
19#endif // __cplusplus19#endif // __cplusplus
20 20 
21+/**
22+ * @brief 在指定 CCU 实例上预约一段连续的 Variable(XN) 资源,并在申请期完成地址映射。
23+ * @param[in] insHandle CCU 实例句柄,资源从该实例的资源池中切分。
24+ * @param[in] dieId 资源归属的 ioDie ID,取值范围 [0, CCU_MAX_IODIE_NUM)。
25+ * @param[in] num 预约的连续资源个数,须大于 0。
26+ * @param[out] varHandle 预约句柄,不可为 nullptr;失败时置 0。
27+ * @return CcuResult。CCU_E_PARA:dieId 或 num 非法;CCU_E_UNAVAIL:无满足 num 的连续块;
28+ * CCU_E_PTR:varHandle 为 nullptr;CCU_E_RUNTIME:地址映射失败。
29+ * @note 句柄生命周期与 insHandle 绑定,无显式释放接口,由 HcommCcuInsDestroy 统一解映射并还池。
30+ */
31+extern CcuResult HcommCcuVariableAlloc(CcuInsHandle insHandle, uint8_t dieId, uint32_t num,
严正行

同步刷新代码仓中的接口资料

likedislike
32+ CcuVariableHandle *varHandle);
33+ 
34+/**
35+ * @brief 预约一段连续的 Event(CKE) 资源,参数与生命周期约束同 HcommCcuVariableAlloc。
36+ * @param[in] insHandle CCU 实例句柄。
37+ * @param[in] dieId 资源归属的 ioDie ID,取值范围 [0, CCU_MAX_IODIE_NUM)。
38+ * @param[in] num 预约的连续资源个数,须大于 0。
39+ * @param[out] eventHandle 预约句柄,不可为 nullptr;失败时置 0。
40+ * @return CcuResult,错误码语义同 HcommCcuVariableAlloc。
41+ * @note CKE 池与 kernel 自身的完成事件共用且单 die 容量有限,大 num 可能总量足够却无连续块。
42+ */
43+extern CcuResult HcommCcuEventAlloc(CcuInsHandle insHandle, uint8_t dieId, uint32_t num,
44+ CcuEventHandle *eventHandle);
45+ 
46+/**
47+ * @brief 查询预约句柄名下第 index 个 Variable(XN) 的进程可访问虚拟地址。
48+ * @param[in] varHandle HcommCcuVariableAlloc 返回的预约句柄。
49+ * @param[in] index 预约段内序号,取值范围 [0, num)。
50+ * @param[out] va 该资源的进程可访问虚拟地址,不可为 nullptr。
51+ * @return CcuResult。CCU_E_NOT_FOUND:句柄不存在;CCU_E_PARA:句柄类型不符或 index 越界;
52+ * CCU_E_PTR:va 为 nullptr。
53+ * @note 地址已在 Alloc 阶段映射并缓存,此处为纯查表;VA 供 CCU 外模块访问,有效期同句柄。
54+ */
55+extern CcuResult HcommCcuVariableGetAddr(CcuVariableHandle varHandle, uint32_t index, uint64_t *va);
56+ 
57+/**
58+ * @brief 查询预约句柄名下第 index 个 Event(CKE) 的进程可访问虚拟地址。
59+ * @param[in] eventHandle HcommCcuEventAlloc 返回的预约句柄。
60+ * @param[in] index 预约段内序号,取值范围 [0, num)。
61+ * @param[out] va 该资源的进程可访问虚拟地址,不可为 nullptr。
62+ * @return CcuResult,错误码与约束同 HcommCcuVariableGetAddr。
63+ */
64+extern CcuResult HcommCcuEventGetAddr(CcuEventHandle eventHandle, uint32_t index, uint64_t *va);
65+ 
21/**66/**
22 * @brief 创建 CCU 实例资源描述符67 * @brief 创建 CCU 实例资源描述符
23 * @param[in] dieId IO Die 编号,取值范围 [0, CCU_MAX_IODIE_NUM)68 * @param[in] dieId IO Die 编号,取值范围 [0, CCU_MAX_IODIE_NUM)
@@ -43,6 +43,11 @@ public:
43 "CcuVariableAlloc: failed");43 "CcuVariableAlloc: failed");
44 }44 }
45 45 
46+ explicit Variable(CcuVariableHandle varHandle, uint32_t index = 0) {
47+ CCU_THROW_IF_FAILED(CcuVariableGetByIndex(varHandle, index, &this->handle),
48+ "CcuVariableGetByIndex: failed");
49+ }
50+ 
46 Variable(const Variable& other) {51 Variable(const Variable& other) {
47 this->handle = other.handle;52 this->handle = other.handle;
48 }53 }
@@ -41,6 +41,12 @@ extern CcuResult CcuBlockBufferAlloc(CcuBufferHandle *bufHandles, uint32_t count
41extern CcuResult CcuVariableCreateByChannel(ChannelHandle channel,41extern CcuResult CcuVariableCreateByChannel(ChannelHandle channel,
42 uint32_t varIndex, CcuVariableHandle *varHandle);42 uint32_t varIndex, CcuVariableHandle *varHandle);
43 43 
44+extern CcuResult CcuVariableGetByIndex(CcuVariableHandle acqHandle,
45+ uint32_t index, CcuVariableHandle *varHandle);
46+ 
47+extern CcuResult CcuEventGetByIndex(CcuEventHandle acqHandle,
48+ uint32_t index, CcuEventHandle *eventHandle);
49+ 
44//Variable操作类 相关接口50//Variable操作类 相关接口
45extern CcuResult CcuVariableAssignImm(CcuVariableHandle resVar, uint64_t immediate);51extern CcuResult CcuVariableAssignImm(CcuVariableHandle resVar, uint64_t immediate);
46extern CcuResult CcuVariableAssignVar(CcuVariableHandle dstVarHandle, CcuVariableHandle srcVarHandle);52extern CcuResult CcuVariableAssignVar(CcuVariableHandle dstVarHandle, CcuVariableHandle srcVarHandle);
@@ -25,6 +25,7 @@
25 25 
26#include "ccu_kernel_mgr.h"26#include "ccu_kernel_mgr.h"
27#include "ccu_instance_mgr.h"27#include "ccu_instance_mgr.h"
28+#include "ccu_var_event_res_mgr.h"
28 29 
29#include "thread.h"30#include "thread.h"
30 31 
@@ -328,6 +329,64 @@ CcuResult HcommCcuKernelLaunch(ThreadHandle threadHandle,
328 return CcuResult::CCU_SUCCESS;329 return CcuResult::CCU_SUCCESS;
329}330}
330 331 
332+// 定位实例资源仓后转发给 CcuVarEventResMgr;预约、地址映射与失败回滚均由该类内部完成
333+static CcuResult CcuAllocVarEventRes(CcuInsHandle insHandle, hcomm::CcuVarEventType type,
334+ uint8_t dieId, uint32_t num, uint64_t &outHandle)
335+{
336+ const uint32_t devLogicId = HcclGetThreadDeviceId();
337+ auto *ccuIns = hcomm::CcuInstanceMgr::GetInstance(devLogicId).Get(insHandle);
338+ CCU_CHK_PTR_NULL(ccuIns);
339+ 
340+ auto *resPack = ccuIns->GetResPack();
341+ CCU_CHK_PTR_NULL(resPack);
342+ 
343+ CCU_CHK_RET(hcomm::CcuVarEventResMgr::GetInstance(devLogicId).Acquire(
344+ insHandle, resPack->GetCcuResRepo(), type, dieId, num, outHandle));
345+ return CcuResult::CCU_SUCCESS;
346+}
347+ 
348+CcuResult HcommCcuVariableAlloc(CcuInsHandle insHandle, uint8_t dieId, uint32_t num,
349+ CcuVariableHandle *varHandle)
350+{
351+ CCU_CHK_PTR_NULL(varHandle);
352+ *varHandle = 0;
353+ CCU_CHK_RET(CcuAllocVarEventRes(insHandle, hcomm::CcuVarEventType::VARIABLE,
严正行

[LOW] HcommCcuVariableAlloc 未包裹异常处理

第 353 行:CCU_CHK_RET(CcuAllocVarEventRes(...)) 调用链最终到达 resMap_.emplace(newHandle, std::move(res)),若 emplace 抛 bad_alloc,异常会穿越 HcommCcuVariableAlloc(extern C 函数)到达调用方。

问题分析:

  • 同文件中 HcommCcuKernelRegister 使用 CCU_EXCEPTION_HANDLE_BEGIN/END 包裹可能抛异常的调用;CcuInstanceMgr::CreateByResDescs 使用 EXCEPTION_CATCH 包裹 emplace。HcommCcuVariableAlloc / HcommCcuEventAlloc 均未做同等防护,风格不一致。
  • C ABI 函数抛异常是未定义行为(C 调用方无法 catch),bad_alloc 虽罕见但非不可能。

建议修复: 用 CCU_EXCEPTION_HANDLE_BEGIN/END 包裹 CcuAllocVarEventRes 调用,失败时返回 CCU_E_INTERNAL。HcommCcuEventAlloc 同理。

likedislike
354+ dieId, num, *varHandle));
355+ return CcuResult::CCU_SUCCESS;
356+}
357+ 
358+CcuResult HcommCcuEventAlloc(CcuInsHandle insHandle, uint8_t dieId, uint32_t num,
359+ CcuEventHandle *eventHandle)
360+{
361+ CCU_CHK_PTR_NULL(eventHandle);
362+ *eventHandle = 0;
363+ CCU_CHK_RET(CcuAllocVarEventRes(insHandle, hcomm::CcuVarEventType::EVENT,
364+ dieId, num, *eventHandle));
365+ return CcuResult::CCU_SUCCESS;
366+}
367+ 
368+CcuResult HcommCcuVariableGetAddr(CcuVariableHandle varHandle, uint32_t index, uint64_t *va)
369+{
370+ CCU_CHK_PTR_NULL(va);
371+ 
372+ const uint32_t devLogicId = HcclGetThreadDeviceId();
373+ CCU_CHK_RET(hcomm::CcuVarEventResMgr::GetInstance(devLogicId).GetSavedAddr(
374+ hcomm::CcuVarEventType::VARIABLE, varHandle, index, *va));
375+ 
376+ return CcuResult::CCU_SUCCESS;
377+}
378+ 
379+CcuResult HcommCcuEventGetAddr(CcuEventHandle eventHandle, uint32_t index, uint64_t *va)
380+{
381+ CCU_CHK_PTR_NULL(va);
382+ 
383+ const uint32_t devLogicId = HcclGetThreadDeviceId();
384+ CCU_CHK_RET(hcomm::CcuVarEventResMgr::GetInstance(devLogicId).GetSavedAddr(
385+ hcomm::CcuVarEventType::EVENT, eventHandle, index, *va));
386+ 
387+ return CcuResult::CCU_SUCCESS;
388+}
389+ 
331CcuResult HcommCcuGetMemToken(uint64_t srcVa, uint64_t size, uint64_t *tokenInfo)390CcuResult HcommCcuGetMemToken(uint64_t srcVa, uint64_t size, uint64_t *tokenInfo)
332{391{
333 CCU_CHK_PTR_NULL(tokenInfo);392 CCU_CHK_PTR_NULL(tokenInfo);
@@ -127,6 +127,26 @@ CcuResult CcuVariableCreateByChannel(ChannelHandle channel, uint32_t varIndex, C
127 return CcuResult::CCU_SUCCESS;127 return CcuResult::CCU_SUCCESS;
128}128}
129 129 
130+CcuResult CcuVariableGetByIndex(CcuVariableHandle acqHandle, uint32_t index, CcuVariableHandle *varHandle)
131+{
132+ CCU_CHK_PTR_NULL(varHandle);
133+ const uint32_t devLogicId = HcclGetThreadDeviceId();
134+ auto kernel = hcomm::CcuKernelMgr::GetInstance(devLogicId).GetCurrentKernel();
135+ CCU_CHK_PTR_NULL(kernel);
136+ CCU_CHK_RET(kernel->VariableCreateByAcquire(acqHandle, index, varHandle));
137+ return CcuResult::CCU_SUCCESS;
138+}
139+ 
140+CcuResult CcuEventGetByIndex(CcuEventHandle acqHandle, uint32_t index, CcuEventHandle *eventHandle)
141+{
142+ CCU_CHK_PTR_NULL(eventHandle);
143+ const uint32_t devLogicId = HcclGetThreadDeviceId();
144+ auto kernel = hcomm::CcuKernelMgr::GetInstance(devLogicId).GetCurrentKernel();
145+ CCU_CHK_PTR_NULL(kernel);
146+ CCU_CHK_RET(kernel->EventCreateByAcquire(acqHandle, index, eventHandle));
147+ return CcuResult::CCU_SUCCESS;
148+}
149+ 
130//Variable操作类 相关接口150//Variable操作类 相关接口
131CcuResult CcuVariableAssignImm(CcuVariableHandle resVar, uint64_t immediate)151CcuResult CcuVariableAssignImm(CcuVariableHandle resVar, uint64_t immediate)
132{152{
@@ -13,6 +13,7 @@ set(src_list
13 ccu_instance.cc13 ccu_instance.cc
14 ccu_res_desc_mgr.cc14 ccu_res_desc_mgr.cc
15 ccu_instance_mgr.cc15 ccu_instance_mgr.cc
16+ ccu_var_event_res_mgr.cc
16)17)
17 18 
18if(TARGET hcomm)19if(TARGET hcomm)
@@ -19,6 +19,7 @@
19 19 
20#include "ccu_res_pack.h"20#include "ccu_res_pack.h"
21#include "ccu_kernel_mgr.h"21#include "ccu_kernel_mgr.h"
22+#include "ccu_var_event_res_mgr.h"
22#include "ccu_res_specs.h"23#include "ccu_res_specs.h"
23#include "ccu_dev_mgr_imp.h"24#include "ccu_dev_mgr_imp.h"
24 25 
@@ -35,6 +36,8 @@ CcuInstance::~CcuInstance()
35 }36 }
36 kernelHandles_.clear();37 kernelHandles_.clear();
37 38 
39+ (void)CcuVarEventResMgr::GetInstance(devLogicId_).ReleaseByInstance(insHandle_);
40+ 
38 resPack_ = nullptr; // 释放instance持有的CCU资源41 resPack_ = nullptr; // 释放instance持有的CCU资源
39 if (ccuDrvHandle_) {42 if (ccuDrvHandle_) {
40 ccuDrvHandle_ = nullptr; // 先减少引用计数,再尝试关闭43 ccuDrvHandle_ = nullptr; // 先减少引用计数,再尝试关闭
@@ -152,6 +155,8 @@ CcuResult CcuInstance::Reset()
152 155 
153 untranslatedKernelHandles_.clear();156 untranslatedKernelHandles_.clear();
154 CCU_CHK_RET(resPack_->Reset());157 CCU_CHK_RET(resPack_->Reset());
158+ 
159+ CCU_CHK_RET(CcuVarEventResMgr::GetInstance(devLogicId_).ExcludeAllocatedFromRepo(insHandle_));
155 return CcuResult::CCU_SUCCESS;160 return CcuResult::CCU_SUCCESS;
156}161}
157 162 
@@ -160,6 +165,11 @@ CcuResPack *CcuInstance::GetResPack()
160 return resPack_.get();165 return resPack_.get();
161}166}
162 167 
168+void CcuInstance::SetHandle(CcuInsHandle insHandle)
169+{
170+ insHandle_ = insHandle;
171+}
172+ 
163// 累加某 die 上某资源 vector 中各 ResInfo::num,得到该 die 该资源的占用数量173// 累加某 die 上某资源 vector 中各 ResInfo::num,得到该 die 该资源的占用数量
164static uint32_t SumResNum(const std::vector<ResInfo> &resInfos, std::string resName)174static uint32_t SumResNum(const std::vector<ResInfo> &resInfos, std::string resName)
165{175{
@@ -42,6 +42,7 @@ public:
42 const CcuResDesc &GetTotalResDescs(uint8_t dieId) const;42 const CcuResDesc &GetTotalResDescs(uint8_t dieId) const;
43 CcuResult SaveKernel(const CcuKernelHandle kernelHandle);43 CcuResult SaveKernel(const CcuKernelHandle kernelHandle);
44 const std::vector<CcuKernelHandle> &GetUntranslatedKernels();44 const std::vector<CcuKernelHandle> &GetUntranslatedKernels();
45+ void SetHandle(CcuInsHandle insHandle);
45 46 
46 CcuResult BeginRegister();47 CcuResult BeginRegister();
47 CcuResult CheckRegistering() const;48 CcuResult CheckRegistering() const;
@@ -55,6 +56,7 @@ private:
55 enum class RegisterState { IDLE, REGISTERING, REGISTER_ABORTED };56 enum class RegisterState { IDLE, REGISTERING, REGISTER_ABORTED };
56 RegisterState registerState_{RegisterState::IDLE};57 RegisterState registerState_{RegisterState::IDLE};
57 int32_t devLogicId_{INT32_MAX};58 int32_t devLogicId_{INT32_MAX};
59+ CcuInsHandle insHandle_{0};
58 std::shared_ptr<hcomm::CcuDrvHandle> ccuDrvHandle_{};60 std::shared_ptr<hcomm::CcuDrvHandle> ccuDrvHandle_{};
59 std::unique_ptr<CcuResPack> resPack_{};61 std::unique_ptr<CcuResPack> resPack_{};
60 std::vector<CcuKernelHandle> kernelHandles_{};62 std::vector<CcuKernelHandle> kernelHandles_{};
@@ -72,6 +72,7 @@ CcuResult CcuInstanceMgr::CreateByInsType(const CcuInstanceType insType, CcuInsH
72 CCU_CHK_RET(instance->InitByInsType(insType));72 CCU_CHK_RET(instance->InitByInsType(insType));
73 73 
74 instanceId_ += 1;74 instanceId_ += 1;
75+ instance->SetHandle(instanceId_);
75 EXCEPTION_CATCH(76 EXCEPTION_CATCH(
76 insMap_.emplace(instanceId_, std::move(instance)),77 insMap_.emplace(instanceId_, std::move(instance)),
77 return CcuResult::CCU_E_INTERNAL);78 return CcuResult::CCU_E_INTERNAL);
@@ -119,6 +120,7 @@ CcuResult CcuInstanceMgr::CreateByResDescs(
119 CCU_CHK_RET(instance->InitByResDescs(descs, descNum));120 CCU_CHK_RET(instance->InitByResDescs(descs, descNum));
120 121 
121 instanceId_ += 1;122 instanceId_ += 1;
123+ instance->SetHandle(instanceId_);
122 EXCEPTION_CATCH(124 EXCEPTION_CATCH(
123 insMap_.emplace(instanceId_, std::move(instance)),125 insMap_.emplace(instanceId_, std::move(instance)),
124 return CcuResult::CCU_E_INTERNAL);126 return CcuResult::CCU_E_INTERNAL);
@@ -138,6 +140,7 @@ CcuResult CcuInstanceMgr::CreateByAllRes(CcuInsHandle &insHandle)
138 CCU_CHK_RET(instance->InitByAllRes());140 CCU_CHK_RET(instance->InitByAllRes());
139 141 
140 instanceId_ += 1;142 instanceId_ += 1;
143+ instance->SetHandle(instanceId_);
141 EXCEPTION_CATCH(144 EXCEPTION_CATCH(
142 insMap_.emplace(instanceId_, std::move(instance)),145 insMap_.emplace(instanceId_, std::move(instance)),
143 return CcuResult::CCU_E_INTERNAL);146 return CcuResult::CCU_E_INTERNAL);
@@ -0,0 +1,576 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "ccu_var_event_res_mgr.h"
12+ 
13+#include <mutex>
14+ 
15+#include "ccu_log.h"
16+#include "hccl_common.h"
17+#include "ccu_common.h"
18+ 
19+#include "ccu_dev_mgr_imp.h"
20+#include "../ccu_device/ccu_res_specs.h"
21+ 
22+#include "rt_external.h"
23+ 
24+namespace hcomm {
25+ 
26+CcuVarEventResMgr &CcuVarEventResMgr::GetInstance(const int32_t deviceLogicId)
27+{
28+ static CcuVarEventResMgr resMgrs[MAX_MODULE_DEVICE_NUM + 1];
29+ 
30+ int32_t devLogicId = deviceLogicId;
31+ if (devLogicId < 0 || static_cast<uint32_t>(devLogicId) >= MAX_MODULE_DEVICE_NUM) {
严正行

[LOW] GetInstance 边界检查逻辑可简化

问题分析:

  • 第 31 行:if (devLogicId < 0 || static_cast<uint32_t>(devLogicId) >= MAX_MODULE_DEVICE_NUM)
  • 这个逻辑是正确的(负值不会被 cast 掩盖),但 static_cast<uint32_t> 的使用略显复杂
  • 可读性稍差,且容易让人误以为负值会有问题

建议修复: 简化为更直观的表达方式,或保持原样(这是代码风格问题,不影响功能)。

src/base_comm/resources/ccu/ccu_instance/ccu_var_event_res_mgr.cc:31

likedislike
32+ HCCL_WARNING("[CcuVarEventResMgr][%s] use the backup device, devLogicId[%d] should be "
33+ "less than %u.", __func__, devLogicId, MAX_MODULE_DEVICE_NUM);
34+ devLogicId = MAX_MODULE_DEVICE_NUM;
35+ }
36+ 
37+ resMgrs[devLogicId].devLogicId_ = devLogicId;
38+ return resMgrs[devLogicId];
39+}
40+ 
41+CcuResult CcuVarEventResMgr::AllocFromPool(std::vector<ResInfo> &pool, uint32_t num,
42+ std::vector<ResInfo> &out)
43+{
44+ for (auto it = pool.begin(); it != pool.end(); ++it) {
45+ if (it->num < num) {
46+ continue;
47+ }
48+ 
49+ out.clear();
50+ out.emplace_back(it->startId, num);
51+ 
52+ if (it->num == num) {
53+ pool.erase(it);
54+ } else {
55+ it->startId += num;
56+ it->num -= num;
57+ }
58+ return CCU_SUCCESS;
59+ }
60+ 
61+ return CCU_E_UNAVAIL;
62+}
63+ 
64+void CcuVarEventResMgr::ReturnToPool(std::vector<ResInfo> &pool, const std::vector<ResInfo> &res)
65+{
66+ for (const auto &info : res) {
67+ if (info.num == 0) {
68+ continue;
69+ }
70+ pool.emplace_back(info.startId, info.num);
严正行严正行

[LOW] ReturnToPool 不合并相邻空闲块

第 70 行:pool.emplace_back(info.startId, info.num); 将归还的资源块直接追加到 pool 末尾,不检查是否与现有空闲块相邻可合并。

问题分析:

  • AllocFromPool 采用 first-fit 搜索连续块。归还时不合并,导致 pool 中出现大量小碎片。
  • 例如池 [0,100) alloc(5) 后 [5,100),release 后 [5,100)+[0,5)。再 alloc(5) 从 [5,100) 切,[0,5) 永远无法满足 num=5 的连续请求。
  • 当前无显式 Release API(生命周期绑 insHandle),碎片化主要发生在 Acquire 失败回滚路径和 Reset 后。实际影响有限,但设计上应考虑合并。

建议修复: ReturnToPool 追加后对 pool 按 startId 排序并合并相邻块。若性能敏感,可惰性合并(仅当 AllocFromPool 找不到连续块时触发一次全量合并)。注意合并需在 mapMutex_ 保护下或 pool 所属 instance 的单线程上下文中执行。

likedislike

[MEDIUM] ReturnToPool 简单追加可能导致长期碎片化

问题分析:

  • 第 70 行 pool.emplace_back(info.startId, info.num); 直接将归还的 block 追加到 pool 末尾
  • 没有对 pool 进行排序或合并相邻 block 的操作
  • 长期运行后,pool 中可能累积大量碎片化的小块,降低 AllocFromPool 找到大块连续资源的效率
  • 例如:释放 [5,3] 和 [8,2] 后,pool 中会有两个不连续的块,但实际上 [5,5] 是一个更大的连续块

建议修复: 在 ReturnToPool 后调用一个合并函数(可选优化),对 pool 排序并合并相邻 block。

注意:这是一个性能优化,不是 correctness 问题。是否需要合并取决于实际碎片化情况。

src/base_comm/resources/ccu/ccu_instance/ccu_var_event_res_mgr.cc:70

likedislike
71+ }
72+}
73+ 
74+static std::vector<ResInfo> *SelectPool(CcuResRepository &resRepo, CcuVarEventType type, uint8_t dieId)
75+{
76+ switch (type) {
77+ case CcuVarEventType::VARIABLE:
78+ return &resRepo.blockXn[dieId];
79+ case CcuVarEventType::EVENT:
80+ return &resRepo.blockCke[dieId];
81+ default:
82+ return nullptr;
83+ }
84+}
85+ 
86+// 由预约资源类型推导runtime资源类型,不支持的类型返回false
87+static bool GetRtResType(CcuVarEventType type, rtDevResType_t &resType)
88+{
89+ switch (type) {
90+ case CcuVarEventType::VARIABLE:
91+ resType = RT_RES_TYPE_CCU_XN;
92+ return true;
93+ case CcuVarEventType::EVENT:
94+ resType = RT_RES_TYPE_CCU_CKE;
95+ return true;
96+ default:
97+ return false;
98+ }
99+}
100+ 
@@ -0,0 +1,110 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#ifndef HCOMM_CCU_VAR_EVENT_RES_MGR_H
12+#define HCOMM_CCU_VAR_EVENT_RES_MGR_H
13+ 
14+#include <shared_mutex>
15+#include <vector>
16+#include <unordered_map>
17+ 
18+#include "ccu_types.h"
19+ 
20+#include "ccu_res_repo.h"
21+ 
22+namespace hcomm {
23+ 
24+enum class CcuVarEventType {
25+ VARIABLE = 0,
26+ EVENT = 1,
27+};
28+ 
29+struct CcuVarEventRes {
30+ CcuInsHandle insHandle{0};
31+ int32_t devLogicId{-1};
严正行

[LOW] CcuVarEventRes::devLogicId 为死字段

第 31 行:int32_t devLogicId{-1}; 在 AllocAndRecord 中被赋值 res.devLogicId = devLogicId_;,但全代码链路中(GetVariableXnId / GetEventCkeId / SaveAddrs / GetSavedAddr / UnmapSavedAddrs / ReleaseByHandle / ReleaseByInstance / ExcludeAllocatedFromRepo)均未读取该字段。

问题分析:

  • 该字段可能是早期设计遗留或预期用于调试/日志,但当前所有日志均使用 CcuVarEventResMgr::devLogicId_(成员变量)而非 res.devLogicId。
  • 死字段增加 struct 体积(4 字节加可能的 padding),在 resMap_ 中每个条目都携带,无实际用途。

建议修复: 若确无使用计划,删除 CcuVarEventRes::devLogicId 字段及其赋值。若保留用于调试,加注释说明用途并在至少一条日志中引用。

likedislike
32+ uint8_t dieId{0};
33+ CcuVarEventType type{CcuVarEventType::VARIABLE};
34+ std::vector<ResInfo> resInfos{};
35+ // 借用指针,指向发起预约的 CcuInstance 名下 CcuResPack 持有的资源仓,本类不持有所有权。
36+ // 有效性依赖既定时序:~CcuInstance 中 ReleaseByInstance 先于 resPack_ 置空执行,
37+ // CcuInstance::Reset 调 ExcludeAllocatedFromRepo 时资源仓亦仍存活。
38+ // 新增读写该指针的路径时,须确认调用点仍在 CcuResPack 析构之前。
39+ CcuResRepository *resRepo{nullptr};
40+ // 申请时对每个资源注册好的进程可访问 VA,下标与资源 index 一一对应
41+ std::vector<uint64_t> vaList{};
42+};
43+ 
44+// 职责:管理通信域内 Variable(XN) / Event(CKE) 预约资源的完整生命周期,涵盖四个阶段——
45+// 从实例资源仓切出连续块、申请期为每个资源注册进程可访问 VA、按 handle 记账、随实例回收。
46+// 四者是同一份资源的先后阶段而非彼此独立的职责,故收敛在同一个类内:Acquire 完成
47+// 「切块 + 映射 + 记账」,ReleaseByHandle / ReleaseByInstance 完成对称的「解映射 + 归还」。
48+//
49+// 为何 Variable 与 Event 合用一个管理器:两类资源共享完全相同的使用模式——一次性预约连续块、
50+// 申请期暴露地址、句柄随通信域销毁统一回收;差异仅在资源池(blockXn / blockCke)与 runtime
51+// 资源类型(RT_RES_TYPE_CCU_XN / RT_RES_TYPE_CCU_CKE)两处,均由 CcuVarEventType 参数化。
52+// 拆成两个类会使上述四阶段流程与回滚逻辑重复一遍,故不拆。
53+//
54+// 与 CcuResIdAllocator 的边界:后者位于设备层,管理自持的 [0, capacity) 完整 id 空间,
55+// 其 resInfos_ 记录的是「已分配块」;本类位于实例层,从上游下发给本通信域的若干段「空闲块」
56+// 中做二次切分,无 capacity 概念。两者数据结构语义相反,不存在替代关系。
57+//
58+// 线程安全契约:
59+// 1) resMap_ 与 handleSeed_ 为 per-device、跨 CcuInstance 共享,全部读写均由 mapMutex_ 保护;
60+// 2) CcuVarEventRes::resRepo 指向的资源池由发起预约的 CcuInstance(经其 CcuResPack)持有,
61+// 不在 mapMutex_ 的保护范围内。CcuInstance 自身不含锁,约定同一 instance 只由单线程串行
62+// 访问,池的并发安全由该约定保证;不同 instance 的池互不相交,跨 instance 并发是安全的。
63+// 因此 ExcludeAllocatedFromRepo 取 shared_lock 是为遍历 resMap_,而非保护池。
64+class CcuVarEventResMgr {
65+public:
66+ static CcuVarEventResMgr &GetInstance(const int32_t deviceLogicId);
67+ 
68+ // 预约一段连续资源并在申请期完成地址映射;任一阶段失败均整笔回滚,仅全程成功才写 handle
69+ CcuResult Acquire(CcuInsHandle insHandle, CcuResRepository &resRepo, CcuVarEventType type,
70+ uint8_t dieId, uint32_t num, uint64_t &handle);
71+ CcuResult GetVariableXnId(uint64_t handle, uint32_t index, uint8_t &dieId, uint32_t &xnId) const;
72+ CcuResult GetEventCkeId(uint64_t handle, uint32_t index, uint8_t &dieId, uint32_t &ckeId) const;
73+ // 保存 Alloc 阶段为该 handle 注册好的全部资源 VA(下标与资源 index 一一对应)
74+ CcuResult SaveAddrs(CcuVarEventType type, uint64_t handle, const std::vector<uint64_t> &vaList);
75+ // 返回 Alloc 阶段已注册的第 index 个资源 VA
76+ CcuResult GetSavedAddr(CcuVarEventType type, uint64_t handle, uint32_t index, uint64_t &va) const;
77+ CcuResult ReleaseByHandle(uint64_t handle);
78+ CcuResult ReleaseByInstance(CcuInsHandle insHandle);
79+ CcuResult ExcludeAllocatedFromRepo(CcuInsHandle insHandle) const;
80+ 
81+private:
82+ explicit CcuVarEventResMgr() = default;
83+ ~CcuVarEventResMgr() = default;
84+ 
85+ CcuVarEventResMgr(const CcuVarEventResMgr &that) = delete;
86+ CcuVarEventResMgr &operator=(const CcuVarEventResMgr &that) = delete;
87+ 
88+ // 校验参数、选池、切出连续块并登记到 resMap_,成功时经 newHandle 返回新句柄
89+ CcuResult AllocAndRecord(CcuInsHandle insHandle, CcuResRepository &resRepo,
90+ CcuVarEventType type, uint8_t dieId, uint32_t num, uint64_t &newHandle);
91+ // 为 handle 名下 num 个资源逐个注册进程可访问 VA 并缓存;失败时解除本次已完成的映射,
92+ // 资源池的归还由 Acquire 与切池动作配对完成
93+ CcuResult RegisterAddrs(CcuVarEventType type, uint64_t handle, uint32_t num);
94+ 
95+ static CcuResult AllocFromPool(std::vector<ResInfo> &pool, uint32_t num,
96+ std::vector<ResInfo> &out);
97+ static void ReturnToPool(std::vector<ResInfo> &pool, const std::vector<ResInfo> &res);
98+ // 释放资源前,将 Alloc 阶段映射的进程可访问 VA 逐个 unmap(仅处理已保存 VA 的资源);
99+ // 返回首个 unmap 失败的错误码,失败不中断,其余资源继续 unmap
100+ static CcuResult UnmapSavedAddrs(const CcuVarEventRes &res);
101+ 
102+ int32_t devLogicId_{-1};
103+ uint64_t handleSeed_{0};
104+ mutable std::shared_timed_mutex mapMutex_;
105+ std::unordered_map<uint64_t, CcuVarEventRes> resMap_{};
106+};
107+ 
108+} // namespace hcomm
109+ 
110+#endif // HCOMM_CCU_VAR_EVENT_RES_MGR_H
@@ -25,6 +25,7 @@
25#include "ccu_rep_context_v1.h"25#include "ccu_rep_context_v1.h"
26#include "ccu_rep_funccall_v1.h"26#include "ccu_rep_funccall_v1.h"
27#include "../../endpoint_pairs/channels/ccu/ccu_urma_channel.h"27#include "../../endpoint_pairs/channels/ccu/ccu_urma_channel.h"
28+#include "../ccu_instance/ccu_var_event_res_mgr.h"
28 29 
29#include "ccu_log.h"30#include "ccu_log.h"
30 31 
@@ -535,6 +536,38 @@ CcuResult CcuKernel::VariableCreateByChannel(ChannelHandle channel, uint32_t var
535 return CcuResult::CCU_SUCCESS;536 return CcuResult::CCU_SUCCESS;
536}537}
537 538 
539+CcuResult CcuKernel::VariableCreateByAcquire(CcuVariableHandle acqHandle, uint32_t index,
540+ CcuVariableHandle *varHandle)
541+{
542+ uint8_t dieId = 0;
543+ uint32_t xnId = 0;
544+ CCU_CHK_RET(CcuVarEventResMgr::GetInstance(HcclGetThreadDeviceId())
545+ .GetVariableXnId(acqHandle, index, dieId, xnId));
546+ 
547+ CcuRep::Variable var(this);
548+ var.Reset(static_cast<uint16_t>(xnId), static_cast<uint16_t>(dieId));
严正行

[MEDIUM] VariableCreateByAcquire 将 uint32_t xnId 截断为 uint16_t

第 548 行:var.Reset(static_cast<uint16_t>(xnId), static_cast<uint16_t>(dieId)); 中 xnId 类型为 uint32_t(来自 GetVariableXnId 出参),但被 static_cast 截断为 uint16_t。若某 die 上 XN 资源 ID 超过 65535,高位被丢弃,Variable 会绑定到错误的资源 ID,导致 kernel 行为异常且无任何告警。

问题分析:

  • GetVariableXnId 返回 uint32_t xnId,资源池 ResInfo::startId/num 均为 uint32_t,全链路用 uint32_t 表达资源 ID。
  • Variable::Reset 接受 uint16_t,此处 static_cast 是静默窄化转换。
  • dieId 为 uint8_t 转 uint16_t 无截断风险,但 xnId 截断是真实风险。

建议修复: 在截断前加范围校验,超限返回错误码而非静默截断:

if (xnId > UINT16_MAX) {
    HCCL_ERROR("[CcuKernel][VariableCreateByAcquire] xnId[%u] exceeds uint16_t range.", xnId);
    return CcuResult::CCU_E_PARA;
}
var.Reset(static_cast<uint16_t>(xnId), static_cast<uint16_t>(dieId));

或在 CcuVarEventResMgr::AllocFromPool 阶段确保资源 ID 不超 uint16_t 范围。

likedislike
549+ CcuVariableHandle handle = ccuVarMap_.size();
550+ ccuVarMap_.emplace(handle, var);
551+ *varHandle = handle;
552+ return CcuResult::CCU_SUCCESS;
553+}
554+ 
555+CcuResult CcuKernel::EventCreateByAcquire(CcuEventHandle acqHandle, uint32_t index,
556+ CcuEventHandle *eventHandle)
557+{
558+ uint8_t dieId = 0;
559+ uint32_t ckeId = 0;
560+ CCU_CHK_RET(CcuVarEventResMgr::GetInstance(HcclGetThreadDeviceId())
561+ .GetEventCkeId(acqHandle, index, dieId, ckeId));
562+ 
563+ CcuRep::CompletedEvent event(this);
564+ event.Reset(static_cast<uint16_t>(ckeId), static_cast<uint16_t>(dieId));
严正行

[MEDIUM] EventCreateByAcquire 将 uint32_t ckeId 截断为 uint16_t

第 564 行:event.Reset(static_cast<uint16_t>(ckeId), static_cast<uint16_t>(dieId)); 中 ckeId 类型为 uint32_t(来自 GetEventCkeId 出参),被 static_cast 截断为 uint16_t。若 CKE 资源 ID 超过 65535,CompletedEvent 会绑定到错误的资源 ID。

问题分析:

  • 与第 548 行 VariableCreateByAcquire 的 xnId 截断完全同构,是同一类静默窄化问题。
  • CKE 池与 kernel 自身的完成事件共用且单 die 容量有限(ccu_res.h 注释),但 ID 空间仍由 uint32_t 表达,截断缺乏防御。

建议修复:

if (ckeId > UINT16_MAX) {
    HCCL_ERROR("[CcuKernel][EventCreateByAcquire] ckeId[%u] exceeds uint16_t range.", ckeId);
    return CcuResult::CCU_E_PARA;
}
event.Reset(static_cast<uint16_t>(ckeId), static_cast<uint16_t>(dieId));
likedislike
565+ CcuEventHandle handle = ccuEventMap_.size();
566+ ccuEventMap_.emplace(handle, event);
567+ *eventHandle = handle;
568+ return CcuResult::CCU_SUCCESS;
569+}
570+ 
538CcuResult CcuKernel::VariableAssignImm(CcuVariableHandle varHandle, uint64_t immediate)571CcuResult CcuKernel::VariableAssignImm(CcuVariableHandle varHandle, uint64_t immediate)
539{572{
540 PLF_CONFIG_INFO(PLF_DATA_OP, "[VariableAssignImm] varHandle=%llu, immediate=%llu", varHandle, immediate);573 PLF_CONFIG_INFO(PLF_DATA_OP, "[VariableAssignImm] varHandle=%llu, immediate=%llu", varHandle, immediate);
@@ -125,6 +125,8 @@ public:
125 CcuResult BlockEventAlloc(CcuEventHandle *eventHandles, uint32_t count);125 CcuResult BlockEventAlloc(CcuEventHandle *eventHandles, uint32_t count);
126 CcuResult BlockBufferAlloc(CcuBufferHandle *bufHandles, uint32_t count);126 CcuResult BlockBufferAlloc(CcuBufferHandle *bufHandles, uint32_t count);
127 CcuResult VariableCreateByChannel(ChannelHandle channel, uint32_t varIndex, CcuVariableHandle *varHandle);127 CcuResult VariableCreateByChannel(ChannelHandle channel, uint32_t varIndex, CcuVariableHandle *varHandle);
128+ CcuResult VariableCreateByAcquire(CcuVariableHandle acqHandle, uint32_t index, CcuVariableHandle *varHandle);
129+ CcuResult EventCreateByAcquire(CcuEventHandle acqHandle, uint32_t index, CcuEventHandle *eventHandle);
128 130 
129 //参数加载类 相关接口131 //参数加载类 相关接口
130 CcuResult LoadArg(CcuVariableHandle varHandle, uint32_t argId);132 CcuResult LoadArg(CcuVariableHandle varHandle, uint32_t argId);
@@ -0,0 +1,41 @@
1+/**
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+ * CANN Open Software License Agreement Version 2.0 (the "License").
5+ * Please refer to the License for details. You may not use this file except in compliance with the License.
6+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+ * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+ * See LICENSE in the root of the software repository for the full text of the License.
9+ */
10+ 
11+#include "ccu_primitives.hpp"
12+#include "ccu_types.h"
13+ 
14+namespace ccu = ::AscendC::ccu;
15+ 
16+struct CcuVarAcquireDemoKernelArg {
17+ CcuVariableHandle acqHandle{0};
18+ uint32_t varNum{0};
19+ CcuEventHandle acqEventHandle{0};
20+ uint32_t eventNum{0};
21+};
22+ 
23+CcuResult CcuVarAcquireDemoKernel(CcuKernelArg arg)
24+{
25+ auto *args = static_cast<CcuVarAcquireDemoKernelArg *>(arg);
26+ 
27+ ccu::Variable sum;
28+ sum = 0;
29+ ccu::Array<ccu::Variable> vars(args->acqHandle, args->varNum);
30+ for (uint32_t i = 0; i < vars.size(); i++) {
31+ vars[i] = i;
32+ sum += vars[i];
33+ }
34+ 
35+ ccu::Array<ccu::Event> events(args->acqEventHandle, args->eventNum);
36+ for (uint32_t i = 0; i < events.size(); i++) {
37+ ccu::EventRecord(events[i]);
38+ ccu::EventWait(events[i]);
39+ }
40+ return CcuResult::CCU_SUCCESS;
41+}
@@ -31,6 +31,7 @@
31#include "ccu_pfe_cfg_mgr.h"31#include "ccu_pfe_cfg_mgr.h"
32#include "ccu_launch.h"32#include "ccu_launch.h"
33#include "ccu_res.h"33#include "ccu_res.h"
34+#include "ccu_var_event_res_mgr.h"
34 35 
35#include "mocks/ccu_device_mock_utils.h"36#include "mocks/ccu_device_mock_utils.h"
36#include "mocks/ccu_channel_mock_utils.h"37#include "mocks/ccu_channel_mock_utils.h"
@@ -47,6 +48,7 @@
47#include "ccu_kernel_impl/ccu_reduce_scatter_mesh1d_demo.h"48#include "ccu_kernel_impl/ccu_reduce_scatter_mesh1d_demo.h"
48#include "ccu_kernel_impl/ccu_reduce_scatter_mesh1d_a6_demo.h"49#include "ccu_kernel_impl/ccu_reduce_scatter_mesh1d_a6_demo.h"
49#include "ccu_kernel_impl/ccu_groupcopy_demo.h"50#include "ccu_kernel_impl/ccu_groupcopy_demo.h"
51+#include "ccu_kernel_impl/ccu_var_acquire_demo.h"
50 52 
51#undef protected53#undef protected
52#undef private54#undef private
@@ -1374,6 +1376,107 @@ TEST_F(HcommCcuControlApiTest, Ut_HcommCcuKernelReduceScatterMesh1d_When_V2_Expe
1374 DestroyCcuResDescs(resDescs);1376 DestroyCcuResDescs(resDescs);
1375}1377}
1376 1378 
1379+TEST_F(HcommCcuControlApiTest, Ut_HcommCcuVariableAllocConsecutive_When_AllFine_Expect_ReturnCcuSUCCESS)
1380+{
1381+ CcuResult ccuRet = CcuResult::CCU_E_RESERVED;
1382+ constexpr uint32_t fakeDevId = MAX_MODULE_DEVICE_NUM - 13;
1383+ constexpr hcomm::CcuVersion fakeCcuVersion = hcomm::CcuVersion::CCU_V1;
1384+ const int32_t fakeDeviceLogicId = MockCcuDeviceEnv(fakeDevId, fakeCcuVersion);
1385+ 
1386+ constexpr auto MS_INS_TPYE = CcuInstanceType::CCU_MS;
1387+ CcuInsHandle insHandle{0};
1388+ ccuRet = HcommCcuInsCreateLegacy(MS_INS_TPYE, &insHandle);
1389+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1390+ 
1391+ constexpr uint8_t fakeDieId = 0;
1392+ constexpr uint32_t varNum = 10;
1393+ CcuVariableHandle varHandle{0};
1394+ ccuRet = HcommCcuVariableAlloc(insHandle, fakeDieId, varNum, &varHandle);
1395+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1396+ 
1397+ for(int i=0; i<varNum; i++) {
1398+ uint64_t startVa = 0;
1399+ ccuRet = HcommCcuVariableGetAddr(varHandle, i, &startVa);
1400+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1401+ std::cout << "[Ut_HcommCcuVariableAllocConsecutive] varHandle=0x" << std::hex << varHandle
1402+ << ", startVa=0x" << startVa << std::dec << ", i=" << i << std::endl;
1403+ }
1404+ 
1405+ auto &resMgr = hcomm::CcuVarEventResMgr::GetInstance(fakeDeviceLogicId);
1406+ auto it = resMgr.resMap_.find(varHandle);
1407+ ASSERT_NE(it, resMgr.resMap_.end());
1408+ const auto &resInfos = it->second.resInfos;
1409+ EXPECT_EQ(resInfos.size(), 1U);
1410+ 
1411+ uint32_t totalNum = 0;
1412+ for (const auto &info : resInfos) {
1413+ std::cout << " consecutive block: startId=" << info.startId << ", num=" << info.num
1414+ << ", xn ids=[";
1415+ for (uint32_t i = 0; i < info.num; i++) {
1416+ std::cout << (info.startId + i);
1417+ if (i + 1 < info.num) {
1418+ std::cout << ", ";
1419+ }
1420+ }
1421+ std::cout << "]" << std::endl;
1422+ totalNum += info.num;
1423+ }
1424+ EXPECT_EQ(totalNum, varNum);
1425+ 
1426+ CcuEventHandle eventHandle{0};
1427+ constexpr uint32_t eventNum = 4;
1428+ ccuRet = HcommCcuEventAlloc(insHandle, fakeDieId, eventNum, &eventHandle);
1429+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1430+ 
1431+ for(int i=0; i<eventNum; i++) {
1432+ uint64_t eventVa = 0;
1433+ ccuRet = HcommCcuEventGetAddr(eventHandle, i, &eventVa);
1434+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1435+ std::cout << "[Ut_HcommCcuVariableAllocConsecutive] eventHandle=0x" << std::hex << eventHandle
1436+ << ", eventVa=0x" << eventVa << std::dec << ", i=" << i << std::endl;
1437+ }
1438+ 
1439+ auto eventIt = resMgr.resMap_.find(eventHandle);
1440+ ASSERT_NE(eventIt, resMgr.resMap_.end());
1441+ const auto &ckeInfos = eventIt->second.resInfos;
1442+ EXPECT_EQ(ckeInfos.size(), 1U);
1443+ for (const auto &info : ckeInfos) {
1444+ std::cout << " consecutive block: startId=" << info.startId << ", num=" << info.num
1445+ << ", xn ids=[";
1446+ for (uint32_t i = 0; i < info.num; i++) {
1447+ std::cout << (info.startId + i);
1448+ if (i + 1 < info.num) {
1449+ std::cout << ", ";
1450+ }
1451+ }
1452+ std::cout << "]" << std::endl;
1453+ }
1454+ 
1455+ ccuRet = HcommCcuKernelRegisterStart(insHandle);
1456+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1457+ 
1458+ CcuVarAcquireDemoKernelArg demoArg{};
1459+ demoArg.acqHandle = varHandle;
1460+ demoArg.varNum = varNum;
1461+ demoArg.acqEventHandle = eventHandle;
1462+ demoArg.eventNum = eventNum;
1463+ auto kernelFunc = reinterpret_cast<void *>(CcuVarAcquireDemoKernel);
1464+ auto kernelArg = static_cast<CcuKernelArg>(&demoArg);
1465+ const void *kernelArgs[] = {kernelArg};
1466+ char *kernelFuncName = "ccu_var_acquire_demo";
1467+ CcuKernelHandle kernelHandle{0};
1468+ constexpr uint32_t kernelArgNum = 1;
1469+ ccuRet = HcommCcuKernelRegister(insHandle, fakeDieId,
1470+ kernelFuncName, kernelFunc, kernelArgs, kernelArgNum, &kernelHandle);
1471+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1472+ 
1473+ ccuRet = HcommCcuKernelRegisterEnd(insHandle);
1474+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1475+ 
1476+ ccuRet = HcommCcuInsDestroy(insHandle);
1477+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1478+}
1479+ 
1377TEST_F(HcommCcuControlApiTest, Ut_HcommCcuKernelNestedInIfIf_When_AllFine_Expect_ReturnCcuSUCCESS)1480TEST_F(HcommCcuControlApiTest, Ut_HcommCcuKernelNestedInIfIf_When_AllFine_Expect_ReturnCcuSUCCESS)
1378{1481{
1379 CcuResult ccuRet = CcuResult::CCU_E_RESERVED;1482 CcuResult ccuRet = CcuResult::CCU_E_RESERVED;
@@ -1427,6 +1530,247 @@ TEST_F(HcommCcuControlApiTest, Ut_HcommCcuKernelNestedInIfIf_When_AllFine_Expect
1427 DestroyCcuResDescs(resDescs);1530 DestroyCcuResDescs(resDescs);
1428}1531}
1429 1532 
1533+TEST_F(HcommCcuControlApiTest, Ut_HcommCcuVariableAndEventAllocAddrGet_When_AllFine_Expect_ReturnCcuSUCCESS)
1534+{
1535+ CcuResult ccuRet = CcuResult::CCU_E_RESERVED;
1536+ constexpr uint32_t fakeDevId = MAX_MODULE_DEVICE_NUM - 14;
1537+ constexpr hcomm::CcuVersion fakeCcuVersion = hcomm::CcuVersion::CCU_V1;
1538+ const int32_t fakeDeviceLogicId = MockCcuDeviceEnv(fakeDevId, fakeCcuVersion);
1539+ 
1540+ // ccuInstance构建(正常在通信域创建中,本用例仅测试hcomm接口)
1541+ constexpr auto MS_INS_TPYE = CcuInstanceType::CCU_MS;
1542+ CcuInsHandle insHandle{0};
1543+ ccuRet = HcommCcuInsCreateLegacy(MS_INS_TPYE, &insHandle);
1544+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1545+ 
1546+ constexpr uint8_t fakeDieId = 0;
1547+ 
1548+ constexpr uint32_t varNum = 8;
1549+ CcuVariableHandle varHandle{0};
1550+ ccuRet = HcommCcuVariableAlloc(insHandle, fakeDieId, varNum, &varHandle);
1551+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1552+ 
1553+ for (uint32_t i = 0; i < varNum; i++) {
1554+ uint64_t startVa = 0;
1555+ ccuRet = HcommCcuVariableGetAddr(varHandle, i, &startVa);
1556+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1557+ EXPECT_NE(startVa, 0U);
1558+ }
1559+ 
1560+ // 越界index应返回失败
1561+ uint64_t invalidVa = 0;
1562+ ccuRet = HcommCcuVariableGetAddr(varHandle, varNum, &invalidVa);
1563+ EXPECT_NE(ccuRet, CcuResult::CCU_SUCCESS);
1564+ 
1565+ constexpr uint32_t eventNum = 4;
1566+ CcuEventHandle eventHandle{0};
1567+ ccuRet = HcommCcuEventAlloc(insHandle, fakeDieId, eventNum, &eventHandle);
1568+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1569+ 
1570+ for (uint32_t i = 0; i < eventNum; i++) {
1571+ uint64_t eventVa = 0;
1572+ ccuRet = HcommCcuEventGetAddr(eventHandle, i, &eventVa);
1573+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1574+ EXPECT_NE(eventVa, 0U);
1575+ }
1576+ 
1577+ // 越界index应返回失败
1578+ uint64_t invalidEventVa = 0;
1579+ ccuRet = HcommCcuEventGetAddr(eventHandle, eventNum, &invalidEventVa);
1580+ EXPECT_NE(ccuRet, CcuResult::CCU_SUCCESS);
1581+ 
1582+ // 校验资源管理器中记录了对应连续资源块
1583+ auto &resMgr = hcomm::CcuVarEventResMgr::GetInstance(fakeDeviceLogicId);
1584+ auto varIt = resMgr.resMap_.find(varHandle);
1585+ ASSERT_NE(varIt, resMgr.resMap_.end());
1586+ uint32_t varTotal = 0;
1587+ for (const auto &info : varIt->second.resInfos) {
1588+ varTotal += info.num;
1589+ }
1590+ EXPECT_EQ(varTotal, varNum);
1591+ 
1592+ auto eventIt = resMgr.resMap_.find(eventHandle);
1593+ ASSERT_NE(eventIt, resMgr.resMap_.end());
1594+ uint32_t eventTotal = 0;
1595+ for (const auto &info : eventIt->second.resInfos) {
1596+ eventTotal += info.num;
1597+ }
1598+ EXPECT_EQ(eventTotal, eventNum);
1599+ 
1600+ ccuRet = HcommCcuInsDestroy(insHandle);
1601+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1602+}
1603+ 
1604+TEST_F(HcommCcuControlApiTest, Ut_HcommCcuVarEventAlloc_When_InvalidParam_Expect_ReturnFailed)
1605+{
1606+ CcuResult ccuRet = CcuResult::CCU_E_RESERVED;
1607+ constexpr uint32_t fakeDevId = MAX_MODULE_DEVICE_NUM - 15;
1608+ constexpr hcomm::CcuVersion fakeCcuVersion = hcomm::CcuVersion::CCU_V1;
1609+ const int32_t fakeDeviceLogicId = MockCcuDeviceEnv(fakeDevId, fakeCcuVersion);
1610+ 
1611+ constexpr auto MS_INS_TPYE = CcuInstanceType::CCU_MS;
1612+ CcuInsHandle insHandle{0};
1613+ ccuRet = HcommCcuInsCreateLegacy(MS_INS_TPYE, &insHandle);
1614+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1615+ 
1616+ constexpr uint8_t fakeDieId = 0;
1617+ constexpr uint32_t validNum = 4;
1618+ CcuVariableHandle varHandle{0xFFFF};
1619+ CcuEventHandle eventHandle{0xFFFF};
1620+ 
1621+ // num 为 0:参数非法,且出参必须被清 0,避免调用方误用脏值
1622+ ccuRet = HcommCcuVariableAlloc(insHandle, fakeDieId, 0, &varHandle);
1623+ EXPECT_EQ(ccuRet, CcuResult::CCU_E_PARA);
1624+ EXPECT_EQ(varHandle, 0U);
1625+ varHandle = 0xFFFF;
1626+ ccuRet = HcommCcuEventAlloc(insHandle, fakeDieId, 0, &eventHandle);
1627+ EXPECT_EQ(ccuRet, CcuResult::CCU_E_PARA);
1628+ EXPECT_EQ(eventHandle, 0U);
1629+ eventHandle = 0xFFFF;
1630+ 
1631+ // dieId 越界:参数非法
1632+ constexpr uint8_t invalidDieId = static_cast<uint8_t>(hcomm::CCU_MAX_IODIE_NUM);
1633+ ccuRet = HcommCcuVariableAlloc(insHandle, invalidDieId, validNum, &varHandle);
1634+ EXPECT_EQ(ccuRet, CcuResult::CCU_E_PARA);
1635+ EXPECT_EQ(varHandle, 0U);
1636+ varHandle = 0xFFFF;
1637+ ccuRet = HcommCcuEventAlloc(insHandle, invalidDieId, validNum, &eventHandle);
1638+ EXPECT_EQ(ccuRet, CcuResult::CCU_E_PARA);
1639+ EXPECT_EQ(eventHandle, 0U);
1640+ 
1641+ // 出参指针为空
1642+ EXPECT_EQ(HcommCcuVariableAlloc(insHandle, fakeDieId, validNum, nullptr), CcuResult::CCU_E_PTR);
1643+ EXPECT_EQ(HcommCcuEventAlloc(insHandle, fakeDieId, validNum, nullptr), CcuResult::CCU_E_PTR);
1644+ 
1645+ // 池中无长度足够的连续块:资源不可用
1646+ constexpr uint32_t hugeNum = 100000;
1647+ varHandle = 0xFFFF;
1648+ ccuRet = HcommCcuVariableAlloc(insHandle, fakeDieId, hugeNum, &varHandle);
1649+ EXPECT_EQ(ccuRet, CcuResult::CCU_E_UNAVAIL);
1650+ EXPECT_EQ(varHandle, 0U);
1651+ eventHandle = 0xFFFF;
1652+ ccuRet = HcommCcuEventAlloc(insHandle, fakeDieId, hugeNum, &eventHandle);
1653+ EXPECT_EQ(ccuRet, CcuResult::CCU_E_UNAVAIL);
1654+ EXPECT_EQ(eventHandle, 0U);
1655+ 
1656+ // AddrGet 的错误路径:句柄不存在 / 句柄类型不符 / 出参指针为空
1657+ uint64_t va = 0;
1658+ EXPECT_EQ(HcommCcuVariableGetAddr(0xDEADBEEF, 0, &va), CcuResult::CCU_E_NOT_FOUND);
1659+ EXPECT_EQ(HcommCcuEventGetAddr(0xDEADBEEF, 0, &va), CcuResult::CCU_E_NOT_FOUND);
1660+ 
1661+ ccuRet = HcommCcuVariableAlloc(insHandle, fakeDieId, validNum, &varHandle);
1662+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1663+ EXPECT_NE(varHandle, 0U);
1664+ EXPECT_EQ(HcommCcuVariableGetAddr(varHandle, 0, nullptr), CcuResult::CCU_E_PTR);
1665+ // 用 variable 的预约句柄去查 event 地址,类型不符应失败
1666+ EXPECT_EQ(HcommCcuEventGetAddr(varHandle, 0, &va), CcuResult::CCU_E_PARA);
1667+ 
1668+ ccuRet = HcommCcuInsDestroy(insHandle);
1669+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1670+}
1671+ 
1672+TEST_F(HcommCcuControlApiTest, Ut_HcommCcuVariableAlloc_When_MapAddrFailed_Expect_RollbackAll)
1673+{
1674+ CcuResult ccuRet = CcuResult::CCU_E_RESERVED;
1675+ constexpr uint32_t fakeDevId = MAX_MODULE_DEVICE_NUM - 16;
1676+ constexpr hcomm::CcuVersion fakeCcuVersion = hcomm::CcuVersion::CCU_V1;
1677+ const int32_t fakeDeviceLogicId = MockCcuDeviceEnv(fakeDevId, fakeCcuVersion);
1678+ 
1679+ constexpr auto MS_INS_TPYE = CcuInstanceType::CCU_MS;
1680+ CcuInsHandle insHandle{0};
1681+ ccuRet = HcommCcuInsCreateLegacy(MS_INS_TPYE, &insHandle);
1682+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1683+ 
1684+ auto *ccuIns = hcomm::CcuInstanceMgr::GetInstance(fakeDeviceLogicId).Get(insHandle);
1685+ ASSERT_NE(ccuIns, nullptr);
1686+ auto *resPack = ccuIns->GetResPack();
1687+ ASSERT_NE(resPack, nullptr);
1688+ 
1689+ constexpr uint8_t fakeDieId = 0;
1690+ const std::vector<hcomm::ResInfo> poolBefore = resPack->GetCcuResRepo().blockXn[fakeDieId];
1691+ auto &resMgr = hcomm::CcuVarEventResMgr::GetInstance(fakeDeviceLogicId);
1692+ const size_t resMapSizeBefore = resMgr.resMap_.size();
1693+ 
1694+ // 第 1 个资源映射成功、第 2 个起失败,触发已映射资源的逐个解映射 + 整笔归还
1695+ MOCKER(rtGetDevResAddress).stubs()
1696+ .will(returnValue(static_cast<rtError_t>(RT_ERROR_NONE)))
1697+ .then(returnValue(static_cast<rtError_t>(0xFFFFFFFF)));
1698+ // 已成功映射的那 1 个资源必须被精确解映射:次数由 TearDown 的 GlobalMockObject::verify 校验
1699+ MOCKER(rtReleaseDevResAddress).expects(exactly(1))
1700+ .will(returnValue(static_cast<rtError_t>(RT_ERROR_NONE)));
1701+ 
1702+ constexpr uint32_t varNum = 4;
1703+ CcuVariableHandle varHandle{0xFFFF};
1704+ ccuRet = HcommCcuVariableAlloc(insHandle, fakeDieId, varNum, &varHandle);
1705+ EXPECT_EQ(ccuRet, CcuResult::CCU_E_RUNTIME);
1706+ 
1707+ // 回滚后:出参不得残留句柄,登记表不得残留记录,资源池必须完全恢复
1708+ EXPECT_EQ(varHandle, 0U);
1709+ EXPECT_EQ(resMgr.resMap_.size(), resMapSizeBefore);
1710+ 
1711+ const std::vector<hcomm::ResInfo> &poolAfter = resPack->GetCcuResRepo().blockXn[fakeDieId];
1712+ uint32_t totalBefore = 0;
1713+ for (const auto &info : poolBefore) {
1714+ totalBefore += info.num;
1715+ }
1716+ uint32_t totalAfter = 0;
1717+ for (const auto &info : poolAfter) {
1718+ totalAfter += info.num;
1719+ }
1720+ EXPECT_EQ(totalAfter, totalBefore);
1721+ 
1722+ ccuRet = HcommCcuInsDestroy(insHandle);
1723+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1724+}
1725+ 
1726+// 前几条用例都经 HcommCcuInsCreateLegacy 建实例,走 CcuInstanceMgr::CreateByInsType;
1727+// 本例改走 HcommCcuInsCreate(落到 CreateByResDescs)。两条路径都必须把实例句柄回填给
1728+// CcuInstance,否则析构时 ReleaseByInstance 拿到的 insHandle_ 为 0,匹配不到任何预约记录,
1729+// 预约的 XN/CKE 既不解映射也不还池——不报错、只泄漏。用「解映射次数」与「登记表是否清空」
1730+// 两个可观测量把这条约束钉住。
1731+TEST_F(HcommCcuControlApiTest, Ut_HcommCcuVarEventAlloc_When_InsCreateByResDescs_Expect_ReleaseOnInsDestroy)
1732+{
1733+ CcuResult ccuRet = CcuResult::CCU_E_RESERVED;
1734+ constexpr uint32_t fakeDevId = MAX_MODULE_DEVICE_NUM - 17;
1735+ constexpr hcomm::CcuVersion fakeCcuVersion = hcomm::CcuVersion::CCU_V1;
1736+ const int32_t fakeDeviceLogicId = MockCcuDeviceEnv(fakeDevId, fakeCcuVersion);
1737+ 
1738+ HcommCcuResDescHandle resDescs[hcomm::CCU_MAX_IODIE_NUM] = {0, 0};
1739+ CreateCcuResDescsPair(resDescs, fakeCcuVersion);
1740+ constexpr uint32_t descNum = 2;
1741+ CcuInsHandle insHandle{0};
1742+ ccuRet = HcommCcuInsCreate(resDescs, descNum, &insHandle);
1743+ ASSERT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1744+ 
1745+ constexpr uint8_t fakeDieId = 0;
1746+ constexpr uint32_t varNum = 4;
1747+ constexpr uint32_t eventNum = 2;
1748+ // 预约期每个资源各注册一次 VA,实例析构时必须一一解映射;次数由 TearDown 的
1749+ // GlobalMockObject::verify 校验,漏调 SetHandle 时这里会是 0 次
1750+ MOCKER(rtReleaseDevResAddress).expects(exactly(varNum + eventNum))
1751+ .will(returnValue(static_cast<rtError_t>(RT_ERROR_NONE)));
1752+ 
1753+ CcuVariableHandle varHandle{0};
1754+ ASSERT_EQ(HcommCcuVariableAlloc(insHandle, fakeDieId, varNum, &varHandle),
1755+ CcuResult::CCU_SUCCESS);
1756+ CcuEventHandle eventHandle{0};
1757+ ASSERT_EQ(HcommCcuEventAlloc(insHandle, fakeDieId, eventNum, &eventHandle),
1758+ CcuResult::CCU_SUCCESS);
1759+ 
1760+ auto &resMgr = hcomm::CcuVarEventResMgr::GetInstance(fakeDeviceLogicId);
1761+ ASSERT_NE(resMgr.resMap_.find(varHandle), resMgr.resMap_.end());
1762+ ASSERT_NE(resMgr.resMap_.find(eventHandle), resMgr.resMap_.end());
1763+ 
1764+ ccuRet = HcommCcuInsDestroy(insHandle);
1765+ EXPECT_EQ(ccuRet, CcuResult::CCU_SUCCESS);
1766+ 
1767+ // 实例销毁后两条预约记录都不得残留
1768+ EXPECT_EQ(resMgr.resMap_.find(varHandle), resMgr.resMap_.end());
1769+ EXPECT_EQ(resMgr.resMap_.find(eventHandle), resMgr.resMap_.end());
1770+ 
1771+ DestroyCcuResDescs(resDescs);
1772+}
1773+ 
1430TEST_F(HcommCcuControlApiTest, Ut_HcommCcuKernelAllGatherMesh1dMem2Mem_When_AllFine_Expect_ReturnCcuSUCCESS)1774TEST_F(HcommCcuControlApiTest, Ut_HcommCcuKernelAllGatherMesh1dMem2Mem_When_AllFine_Expect_ReturnCcuSUCCESS)
1431{1775{
1432 CcuResult ccuRet = CcuResult::CCU_E_RESERVED;1776 CcuResult ccuRet = CcuResult::CCU_E_RESERVED;
@@ -66,5 +66,14 @@ rtError_t rtReleaseDevResAddress(rtDevResInfo * const resInfo)
66 66 
67rtError_t rtGetDevResAddress(rtDevResInfo * const resInfo, rtDevResAddrInfo * const addrInfo)67rtError_t rtGetDevResAddress(rtDevResInfo * const resInfo, rtDevResAddrInfo * const addrInfo)
68{68{
69+ if (resInfo == nullptr || addrInfo == nullptr ||
70+ addrInfo->resAddress == nullptr || addrInfo->len == nullptr) {
71+ return static_cast<rtError_t>(0xFFFFFFFF);
72+ }
73+ // 合成一个非0映射地址,按resId偏移,便于UT校验
74+ constexpr uint64_t stubResBaseAddr = 0x100000000ULL;
75+ constexpr uint64_t stubResPerSize = 0x1000ULL;
76+ *addrInfo->resAddress = stubResBaseAddr + static_cast<uint64_t>(resInfo->resId) * stubResPerSize;
77+ *addrInfo->len = static_cast<uint32_t>(stubResPerSize);
69 return RT_ERROR_NONE;78 return RT_ERROR_NONE;
70}79}