Line data Source code
1 : /**
2 : * Copyright (c) 2025 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_res_allocator.h"
12 :
13 : #include <climits>
14 :
15 : #include "ccu_res_specs.h"
16 :
17 : namespace hcomm {
18 :
19 753 : HcclResult CcuResIdAllocator::Alloc(const uint32_t num, const bool consecutive,
20 : std::vector<ResInfo> &allocatedResInfos, const std::string &dfxInfo)
21 : {
22 753 : CHK_PRT_RET(num == 0,
23 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, request num is 0.", __func__),
24 : HcclResult::HCCL_E_PARA);
25 :
26 752 : std::unique_lock<std::mutex> lock(innerMutex_);
27 : // 快速判断是否可以分配
28 752 : const uint32_t freeSize = capacity_ - allocatedSize_;
29 752 : if (num > freeSize) {
30 2 : HCCL_WARNING("[CcuResIdAllocator][%s] failed, resType[%s], requested num[%u] exceeds "
31 : "currently free size[%u].",
32 : __func__, dfxInfo.c_str(), num, freeSize);
33 2 : HCCL_RUN_INFO("Insufficient CCU Resource: %s, requestNum[%u], freeNum[%u].", dfxInfo.c_str(), num, freeSize);
34 2 : return HcclResult::HCCL_E_UNAVAIL;
35 : }
36 :
37 750 : std::vector<ResInfo> newResInfos;
38 750 : uint32_t leftNum = num;
39 750 : uint32_t tryStartId = 0;
40 : // 对于要求连续的资源需要提供一个足够大小的空闲块
41 : // 对于非连续需要每次不为 0
42 750 : const uint32_t limitSize = consecutive ? num - 1 : 0;
43 : // 顺序优先分配,遍历已分配的连续块,寻找当前块与下个块之间是否有足够大小空间
44 750 : resInfos_.emplace_back(capacity_, 0); // 临时添加一个尾资源,简化判断逻辑
45 1014 : for (size_t i = 0; i < resInfos_.size(); i++) {
46 1013 : const auto &resInfo = resInfos_[i];
47 1013 : uint32_t partNum = std::min(resInfo.startId - tryStartId, leftNum);
48 1013 : if (partNum > limitSize) {
49 749 : newResInfos.emplace_back(tryStartId, partNum); // 该空闲块足够大,分配
50 749 : leftNum -= partNum;
51 749 : if (leftNum == 0) {
52 749 : break;
53 : }
54 : }
55 264 : tryStartId = resInfo.startId + resInfo.num; // 更新当前块起始位置
56 : }
57 750 : resInfos_.pop_back(); // 删除临时添加的尾资源
58 : // 只有连续要求的资源才可能剩余,此时分配失败,新块为空
59 750 : if (leftNum != 0) {
60 1 : HCCL_WARNING("[CcuResIdAllocator][%s] failed, no enough consecutive free "
61 : "resource ids for requested num[%u].", __func__, num);
62 1 : HCCL_RUN_INFO("Insufficient CCU Resource: consecutived %s, requestNum[%u], freeNum[%u].",
63 : dfxInfo.c_str(), num, freeSize);
64 1 : return HcclResult::HCCL_E_UNAVAIL;
65 : }
66 :
67 749 : allocatedSize_ += num;
68 749 : AllocResInfo(newResInfos); // 将分配的所有资源记录
69 749 : allocatedResInfos = newResInfos;
70 749 : return HcclResult::HCCL_SUCCESS;
71 752 : }
72 :
73 749 : void CcuResIdAllocator::AllocResInfo(std::vector<ResInfo> newResInfos)
74 : {
75 749 : if (resInfos_.empty()) { // 首次分配直接添加块
76 486 : resInfos_.emplace_back(newResInfos.front());
77 486 : return;
78 : }
79 : // 内部变量始终维护最简的连续块,对需要合并的块更新
80 263 : size_t newIdx = 0;
81 263 : size_t idx = 0;
82 526 : while (newIdx < newResInfos.size() && idx < resInfos_.size()) {
83 263 : auto &newResInfo = newResInfos[newIdx];
84 263 : auto &resInfo = resInfos_[idx];
85 : // 跳过无关的资源块,使得resInfo是newResInfo的后续块
86 263 : if (newResInfo.startId >= resInfo.startId) {
87 262 : idx++;
88 262 : continue;
89 : }
90 : // 检查当前块是否与后续块连续,如果连续则合并
91 1 : if (newResInfo.startId == resInfo.startId - newResInfo.num) {
92 1 : newResInfo.num += resInfo.num;
93 1 : resInfos_.erase(resInfos_.begin() + idx);
94 : }
95 : // 如果当前块是首块则插入首块
96 1 : if (idx == 0) {
97 1 : resInfos_.insert(resInfos_.begin(), newResInfo);
98 1 : newIdx++;
99 1 : continue;
100 : }
101 : // 分配保证如果当前块不是首块则一定与前一个块连续,更新前一个块
102 0 : resInfos_[idx - 1].num += newResInfo.num;
103 0 : newIdx++;
104 : }
105 : // 如果有剩余块一定与最后一个块连续,更新最后一个块
106 263 : if (newIdx < newResInfos.size()) {
107 262 : resInfos_.back().num += newResInfos.back().num;
108 : }
109 : }
110 :
111 240 : static HcclResult CheckReleasePara(const uint32_t startId, const uint32_t num,
112 : const uint32_t capacity)
113 : {
114 240 : CHK_PRT_RET(num == 0,
115 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource num is 0.", __func__),
116 : HcclResult::HCCL_E_PARA);
117 :
118 239 : CHK_PRT_RET(num > capacity,
119 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource num[%u] "
120 : "is greater than capacity[%u]", __func__, num, capacity),
121 : HcclResult::HCCL_E_PARA);
122 :
123 238 : CHK_PRT_RET(startId > capacity - num,
124 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource startId[%u] "
125 : "num[%u] capacity[%u]", __func__, startId, num, capacity),
126 : HcclResult::HCCL_E_PARA);
127 :
128 237 : return HcclResult::HCCL_SUCCESS;
129 : }
130 :
131 240 : HcclResult CcuResIdAllocator::Release(const uint32_t startId, const uint32_t num)
132 : {
133 240 : CHK_RET(CheckReleasePara(startId, num, capacity_));
134 :
135 237 : std::unique_lock<std::mutex> lock(innerMutex_);
136 :
137 : // 找到需要释放的资源块
138 237 : const size_t resIndex = FindReleaseResIndex(startId);
139 237 : CHK_PRT_RET(resIndex >= resInfos_.size(),
140 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource startId[%u] num[%u] "
141 : "has not been allocated yet. ", __func__, startId, num),
142 : HcclResult::HCCL_E_PARA);
143 :
144 : // 判断申请释放的资源是否越界
145 236 : const auto &resInfo = resInfos_[resIndex];
146 236 : uint32_t allocatedNum = resInfo.startId + resInfo.num - startId;
147 236 : CHK_PRT_RET(num > allocatedNum,
148 : HCCL_ERROR("[CcuResIdAllocator][%s] failed, resource num[%u] is greater "
149 : "than the allocated num[%u].", __func__, num, allocatedNum),
150 : HcclResult::HCCL_E_PARA);
151 :
152 : // 将资源块释放并更新
153 235 : ReleaseResInfo(resIndex, startId, num);
154 235 : return HcclResult::HCCL_SUCCESS;
155 237 : }
156 :
157 237 : size_t CcuResIdAllocator::FindReleaseResIndex(const uint32_t startId) const
158 : {
159 237 : size_t resIndex = 0;
160 237 : const size_t maxIndex = resInfos_.size();
161 237 : while (resIndex < maxIndex) {
162 236 : const auto &resInfo = resInfos_[resIndex];
163 236 : if (startId >= resInfo.startId + resInfo.num) {
164 0 : resIndex++;
165 0 : continue;
166 : }
167 236 : if (startId >= resInfo.startId) {
168 236 : break; // 资源id属于该资源块
169 : }
170 0 : return maxIndex; // 无法找到已分配资源块,返回错误索引
171 : }
172 237 : return resIndex;
173 : }
174 :
175 235 : void CcuResIdAllocator::ReleaseResInfo(const size_t resIndex,
176 : const uint32_t startId, const uint32_t num)
177 : {
178 235 : allocatedSize_ -= num;
179 :
180 235 : auto &resInfo = resInfos_[resIndex];
181 : // 释放的资源在资源块起始部分
182 235 : if (startId == resInfo.startId) {
183 : // 恰好是整块资源,则全部释放
184 27 : if (num == resInfo.num) {
185 24 : resInfos_.erase(resInfos_.begin() + resIndex);
186 234 : return;
187 : }
188 : // 非整块资源则更新资源块起始位置和大小
189 3 : resInfo.startId += num;
190 3 : resInfo.num -= num;
191 3 : return;
192 : }
193 :
194 208 : uint32_t leftNum = startId - resInfo.startId;
195 208 : uint32_t rightNum = resInfo.num - leftNum - num;
196 : // 释放的资源在资源块末尾,更新资源块大小
197 208 : if (rightNum == 0) {
198 207 : resInfo.num -= num;
199 207 : return;
200 : }
201 : // 释放的资源在资源块中间,拆分为两个资源块
202 1 : resInfo.num = leftNum; // 左部分块更新数据
203 : // 右部分块需要新增
204 1 : resInfos_.emplace(resInfos_.begin() + resIndex + 1, startId + num, rightNum);
205 : }
206 :
207 74 : HcclResult CcuResAllocator::Init()
208 : {
209 74 : auto& ccuResSpecs = CcuResSpecifications::GetInstance(devLogicId_);
210 : // 获取静态定义的资源规格查询函数列表,遍历构造
211 74 : uint32_t capacity = 0;
212 666 : for (const auto &pair : CcuResSpecifications::GET_RES_SPEC_FUNC_ARRAY) {
213 592 : const ResType resType = pair.first;
214 592 : const CcuResSpecifications::GetResSpecFunc getFunc = pair.second;
215 592 : (void)(ccuResSpecs.*getFunc)(dieId_, capacity); // 获取失败时容量为 0,后续分配按资源不足处理
216 592 : std::unique_ptr<CcuResIdAllocator> allocatorPtr = nullptr;
217 592 : HCCL_RUN_INFO("[CcuResAllocator][%s] resType[%s], capacity[%u]",
218 : __func__, resType.Describe().c_str(), capacity);
219 592 : allocatorPtr.reset((new (std::nothrow) CcuResIdAllocator(capacity)));
220 592 : CHK_PTR_NULL(allocatorPtr);
221 592 : idAllocatorMap_[static_cast<uint8_t>(resType)] = std::move(allocatorPtr);
222 592 : }
223 :
224 74 : (void)ccuResSpecs.GetXnNum(dieId_, xnSpecNum_);
225 74 : (void)ccuResSpecs.GetCountXnNum(dieId_, countXnSpecNum_);
226 74 : return HcclResult::HCCL_SUCCESS;
227 : }
228 :
229 589 : HcclResult CcuResAllocator::Alloc(const ResType resType, const uint32_t num,
230 : const bool consecutive, std::vector<ResInfo> &resInfos)
231 : {
232 589 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
233 589 : if (resTypeIter == idAllocatorMap_.end()) {
234 1 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
235 : __func__, resType.Describe().c_str());
236 1 : return HcclResult::HCCL_E_PARA;
237 : }
238 588 : return resTypeIter->second->Alloc(num, consecutive, resInfos, resType.Describe());
239 : }
240 :
241 204 : HcclResult CcuResAllocator::Release(const ResType resType, const uint32_t startId,
242 : const uint32_t num)
243 : {
244 204 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
245 204 : if (resTypeIter == idAllocatorMap_.end()) {
246 1 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
247 : __func__, resType.Describe().c_str());
248 1 : return HcclResult::HCCL_E_PARA;
249 : }
250 203 : return resTypeIter->second->Release(startId, num);
251 : }
252 :
253 1 : std::string CcuResIdAllocator::Describe() const
254 : {
255 : return Hccl::StringFormat("CcuResIdAllocator[capacity=%u, allocatedSize=%u, "
256 1 : "resInfos_size=%u]", capacity_, allocatedSize_, resInfos_.size());
257 : }
258 :
259 1 : std::string CcuResAllocator::Describe() const
260 : {
261 : return Hccl::StringFormat("CcuResAllocator[devLogicId=%u, dieId=%u, "
262 1 : "idAllocatorSize=[%u]]", devLogicId_, dieId_, idAllocatorMap_.size());
263 : }
264 :
265 :
266 3 : HcclResult CcuResAllocator::AllocCountXn(const uint32_t num, ResInfo &resInfo)
267 : {
268 3 : constexpr ResType resType = ResType::COUNT_XN;
269 3 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
270 3 : if (resTypeIter == idAllocatorMap_.end()) {
271 0 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
272 : __func__, resType.Describe().c_str());
273 0 : return HcclResult::HCCL_E_PARA;
274 : }
275 :
276 3 : std::vector<ResInfo> resInfos;
277 6 : auto ret = resTypeIter->second->Alloc(num, true, resInfos);
278 3 : if (ret == HcclResult::HCCL_E_UNAVAIL) {
279 1 : HCCL_WARNING("[CcuResAllocator][%s] failed, count xn resources are unavailable, ",
280 : "retry to allocate with normal xn resources, num[%u], devLogicId[%d].",
281 : __func__, num, devLogicId_);
282 1 : ret = Alloc(ResType::XN, num, true, resInfos);
283 1 : if (ret == HcclResult::HCCL_SUCCESS) {
284 1 : resInfo = resInfos[0]; // 连续分配成功时,一定只有一个元素
285 : }
286 1 : return ret;
287 : }
288 2 : CHK_RET(ret);
289 : // CountXn编号切换为全局Xn编号
290 2 : resInfo.startId = resInfos[0].startId + xnSpecNum_;
291 2 : resInfo.num = resInfos[0].num;
292 2 : return HcclResult::HCCL_SUCCESS;
293 3 : }
294 :
295 2 : HcclResult CcuResAllocator::ReleaseCountXn(const uint32_t startId, const uint32_t num)
296 : {
297 2 : if (startId < xnSpecNum_) { // 未超过Xn规格则为普通Xn
298 1 : return Release(ResType::XN, startId, num);
299 : }
300 :
301 1 : constexpr ResType resType = ResType::COUNT_XN;
302 1 : auto resTypeIter = idAllocatorMap_.find(static_cast<uint8_t>(resType));
303 1 : if (resTypeIter == idAllocatorMap_.end()) {
304 0 : HCCL_ERROR("[CcuResAllocator][%s] failed, invalid resource type[%s].",
305 : __func__, resType.Describe().c_str());
306 0 : return HcclResult::HCCL_E_PARA;
307 : }
308 1 : return resTypeIter->second->Release(startId - xnSpecNum_, num);
309 : }
310 : } // namespace hcomm
|