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 "phy_topo_builder.h"
12 : #include "log.h"
13 : #include "json_parser.h"
14 : #include "exception_util.h"
15 : #include "null_ptr_exception.h"
16 : #include "invalid_params_exception.h"
17 : #include "adapter_error_manager_pub.h"
18 :
19 : namespace Hccl {
20 :
21 48 : PhyTopoBuilder& PhyTopoBuilder::GetInstance()
22 : {
23 48 : static PhyTopoBuilder phyTopoBuilder;
24 48 : return phyTopoBuilder;
25 : }
26 :
27 : // 根据nodeType和localId创建节点
28 70 : std::shared_ptr<PhyTopo::Node> CreateNode(const PhyTopo::Node::NodeType nodeType, const LocalId localId)
29 : {
30 70 : if (nodeType == PhyTopo::Node::NodeType::PEER) {
31 54 : return std::make_shared<PhyTopo::Peer>(localId);
32 : } else {
33 16 : return std::make_shared<PhyTopo::Fabric>();
34 : }
35 : }
36 :
37 15 : void PhyTopoBuilder::Build(const std::string& topoPath)
38 : {
39 15 : std::lock_guard<std::mutex> lock(phyTopoMutex);
40 :
41 15 : if (PhyTopo::GetInstance()->IsInitFinished()) {
42 33 : HCCL_INFO("PhyTopo has been initialized and does not need to be rebuilt");
43 11 : return;
44 : }
45 :
46 4 : if (topoPath.empty()) {
47 0 : RPT_INPUT_ERR(
48 : true, "EI0004", std::vector<std::string>({"ranktable_path", "error_reason"}),
49 : std::vector<std::string>(
50 : {"Please check the path configuration of the topo json file.",
51 : "The rankTable file path does not exist, the permission is insufficient, or the JSON format is "
52 : "incorrect."}));
53 0 : THROW<InvalidParamsException>("[PhyTopoBuilder::%s] Topo path is empty.", __func__);
54 : }
55 :
56 12 : HCCL_DEBUG("[PhyTopoBuilder::%s]Start to build physic topo.", __func__);
57 :
58 4 : auto topoInfo = LoadTopoInfo(topoPath);
59 : // 根据topoInfo,按netLayer构造Graph
60 10 : for (const auto& iter : topoInfo->edges) {
61 6 : auto netLayer = iter.first;
62 6 : auto graph = CreateGraph(iter.second);
63 6 : PhyTopo::GetInstance()->AddTopoGraph(netLayer, graph);
64 18 : HCCL_DEBUG("[PhyTopoBuilder::%s]Build netLayer[%u] topo graph success.", __func__, netLayer);
65 6 : }
66 :
67 : // 遍历peerList,把peerList中的localId构造peer节点,加到layer-0的graph中
68 4 : auto graph = PhyTopo::GetInstance()->GetTopoGraph(0);
69 4 : if (graph == nullptr) {
70 0 : HCCL_INFO("[PhyTopoBuilder::%s] layer0 graph is nullptr, build now", __func__);
71 0 : graph = make_shared<Graph<PhyTopo::Node, PhyTopo::Link>>();
72 0 : PhyTopo::GetInstance()->AddTopoGraph(0, graph);
73 : }
74 19 : for (const auto& iter : topoInfo->peers) {
75 : // 判断当前layer0的graph有没有这个节点
76 15 : if (!graph->HasNode(iter.localId)) {
77 0 : auto node = CreateNode(PhyTopo::Node::NodeType::PEER, iter.localId);
78 0 : graph->AddNode(iter.localId, node);
79 0 : }
80 : }
81 :
82 4 : PhyTopo::GetInstance()->InitFinish();
83 :
84 12 : HCCL_DEBUG("[PhyTopoBuilder::%s]build physic topo success.", __func__);
85 4 : PhyTopo::GetInstance()->Dump();
86 15 : }
87 :
88 7 : std::shared_ptr<TopoInfo> PhyTopoBuilder::LoadTopoInfo(const std::string& topoPath)
89 : {
90 7 : std::shared_ptr<TopoInfo> topoInfo = std::make_shared<TopoInfo>();
91 : // 检查是否为非法路径以及size的大小。
92 : struct stat fileStat;
93 7 : if (stat(topoPath.c_str(), &fileStat) != 0) {
94 2 : HCCL_ERROR(
95 : "[PhyTopoBuilder][LoadTopoInfo] Get file stat failed, file path:%s, errno:%d, error: %s", topoPath.c_str(),
96 : errno, strerror(errno));
97 2 : THROW<InvalidParamsException>(
98 : "[PhyTopoBuilder][LoadTopoInfo]Get file stat failed, file path:%s, errno:%d, error: %s", topoPath.c_str(),
99 2 : errno, strerror(errno));
100 : }
101 :
102 5 : u64 topoFileSize = static_cast<u64>(fileStat.st_size);
103 5 : if (topoFileSize > SUPPORT_MAX_TOPOFILE_SIZE || topoFileSize <= 0) {
104 1 : HCCL_ERROR(
105 : "[PhyTopoBuilder][LoadTopoInfo] topoFileSize size: %llu, topoFile must be greater than 0 and less than %u",
106 : topoFileSize, SUPPORT_MAX_TOPOFILE_SIZE);
107 2 : THROW<InvalidParamsException>(StringFormat(
108 : "[PhyTopoBuilder][LoadTopoInfo]file %s size (%llu bytes) exceeds max allowed size (%u bytes)",
109 : topoPath.c_str(), topoFileSize, SUPPORT_MAX_TOPOFILE_SIZE));
110 : }
111 :
112 : JsonParser topoParser;
113 4 : topoParser.ParseFile(topoPath, *topoInfo);
114 4 : topoInfo_ = topoInfo;
115 4 : return topoInfo;
116 3 : }
117 :
118 52 : shared_ptr<PhyTopo::Link> CreatePeer2PeerLink(const LinkParams& params)
119 : {
120 52 : LinkType linkType = LinkType::PEER2PEER;
121 : auto srcIface
122 52 : = std::make_shared<PhyTopo::ConnInterface>(params.localAPorts, params.position, linkType, params.protocols);
123 : auto dstIface
124 52 : = std::make_shared<PhyTopo::ConnInterface>(params.localBPorts, params.position, linkType, params.protocols);
125 52 : PhyTopo::LinkAttributes linkAttrs;
126 52 : linkAttrs.linktype = linkType;
127 52 : linkAttrs.protocols = params.protocols;
128 : auto link = std::make_shared<PhyTopo::Link>(
129 52 : params.srcNode, params.dstNode, linkAttrs, params.topoType, params.topoInstanceId);
130 52 : link->SetSourceIface(srcIface);
131 52 : params.srcNode->AddConnInterface(srcIface);
132 52 : link->SetTargetIface(dstIface);
133 52 : params.dstNode->AddConnInterface(dstIface);
134 104 : return {link};
135 52 : }
136 :
137 116 : shared_ptr<PhyTopo::Link> CreatePeer2NetLink(const LinkParams& params)
138 : {
139 116 : LinkType linkType = LinkType::PEER2NET;
140 : auto srcIface
141 116 : = std::make_shared<PhyTopo::ConnInterface>(params.localAPorts, params.position, linkType, params.protocols);
142 : // fabric 没有ports
143 116 : std::set<std::string> ports{};
144 116 : auto dstIface = std::make_shared<PhyTopo::ConnInterface>(ports, params.position, linkType, params.protocols);
145 116 : PhyTopo::LinkAttributes linkAttrs;
146 116 : linkAttrs.linktype = linkType;
147 116 : linkAttrs.protocols = params.protocols;
148 : auto link = std::make_shared<PhyTopo::Link>(
149 116 : params.srcNode, params.dstNode, linkAttrs, params.topoType, params.topoInstanceId);
150 116 : link->SetSourceIface(srcIface);
151 116 : params.srcNode->AddConnInterface(srcIface);
152 116 : link->SetTargetIface(dstIface);
153 116 : params.dstNode->AddConnInterface(dstIface);
154 232 : return {link};
155 116 : }
156 :
157 : // 根据链路类型建链
158 168 : shared_ptr<PhyTopo::Link> CreateLink(const LinkType linkType, const LinkParams& params)
159 : {
160 168 : if (linkType == LinkType::PEER2PEER) {
161 52 : return CreatePeer2PeerLink(params);
162 : } else {
163 116 : return CreatePeer2NetLink(params);
164 : }
165 : }
166 :
167 168 : NodeId GetNodeId(const PhyTopo::Node::NodeType nodeType, LocalId localId)
168 : {
169 168 : switch (nodeType) {
170 110 : case PhyTopo::Node::NodeType::PEER:
171 110 : return PhyTopo::Peer::GetId(localId);
172 58 : case PhyTopo::Node::NodeType::FABRIC:
173 58 : return PhyTopo::Fabric::GetId();
174 0 : default:
175 0 : THROW<InvalidParamsException>(StringFormat("[PhyTopoBuilder]Invalid NodeType."));
176 : return 0;
177 : }
178 : }
179 :
180 : std::shared_ptr<Graph<PhyTopo::Node, PhyTopo::Link>>
181 21 : PhyTopoBuilder::CreateGraph(const std::vector<EdgeInfo>& edges) const
182 : {
183 21 : auto graph = std::make_shared<Graph<PhyTopo::Node, PhyTopo::Link>>();
184 :
185 105 : for (const auto& edgeInfo : edges) {
186 84 : std::shared_ptr<PhyTopo::Node> nodeA;
187 84 : std::shared_ptr<PhyTopo::Node> nodeB;
188 :
189 : // 生成 nodeAId,localA 始终为 PEER 类型
190 84 : NodeId nodeAId = GetNodeId(PhyTopo::Node::NodeType::PEER, edgeInfo.localA);
191 : // 获取或创建 nodeA
192 84 : if (!graph->HasNode(nodeAId)) {
193 35 : nodeA = CreateNode(PhyTopo::Node::NodeType::PEER, edgeInfo.localA);
194 35 : graph->AddNode(nodeAId, nodeA);
195 61 : HCCL_DEBUG("[PhyTopoBuilder::%s] Add node[%llu] success.", __func__, nodeAId);
196 : } else {
197 : // 使用 TraverseNode 查找 nodeA
198 49 : graph->TraverseNode([&nodeA, nodeAId](NodeId id, const std::shared_ptr<PhyTopo::Node>& n) {
199 185 : if (id == nodeAId) {
200 49 : nodeA = n;
201 : }
202 185 : });
203 49 : if (nodeA == nullptr) {
204 0 : HCCL_ERROR("[PhyTopoBuilder::CreateGraph] nodeAId [%llu] not found.", nodeAId);
205 0 : THROW<NullPtrException>(
206 0 : StringFormat("[PhyTopoBuilder::CreateGraph] Unable to find node [%llu].", nodeAId));
207 : }
208 : }
209 :
210 : // 根据 linkType 生成 nodeBId
211 84 : PhyTopo::Node::NodeType nodeBType;
212 84 : if (edgeInfo.linkType == LinkType::PEER2PEER) {
213 26 : nodeBType = PhyTopo::Node::NodeType::PEER;
214 : } else {
215 58 : nodeBType = PhyTopo::Node::NodeType::FABRIC;
216 : }
217 :
218 84 : NodeId nodeBId = GetNodeId(nodeBType, edgeInfo.localB);
219 : // 获取或创建 nodeB
220 84 : if (!graph->HasNode(nodeBId)) {
221 35 : nodeB = CreateNode(nodeBType, edgeInfo.localB);
222 35 : graph->AddNode(nodeBId, nodeB);
223 63 : HCCL_DEBUG("[PhyTopoBuilder::%s] Add node[%llu] success.", __func__, nodeBId);
224 : } else {
225 : // 使用 TraverseNode 查找 nodeB
226 49 : graph->TraverseNode([&nodeB, nodeBId](NodeId id, const std::shared_ptr<PhyTopo::Node>& n) {
227 206 : if (id == nodeBId) {
228 49 : nodeB = n;
229 : }
230 206 : });
231 49 : if (nodeB == nullptr) {
232 0 : HCCL_ERROR("[PhyTopoBuilder::CreateGraph] nodeBId [%llu] not found.", nodeBId);
233 0 : THROW<NullPtrException>(
234 0 : StringFormat("[PhyTopoBuilder::CreateGraph] Unable to find node [%llu].", nodeBId));
235 : }
236 : }
237 :
238 : // 双向分别创建link
239 : LinkParams abLinkParams{
240 : nodeA,
241 : nodeB,
242 84 : edgeInfo.localAPorts,
243 84 : edgeInfo.localBPorts,
244 : edgeInfo.position,
245 : edgeInfo.topoType,
246 84 : edgeInfo.topoInstId,
247 84 : edgeInfo.protocols};
248 84 : auto abLinks = CreateLink(edgeInfo.linkType, abLinkParams);
249 84 : graph->AddEdge(nodeAId, nodeBId, abLinks);
250 : LinkParams baLinkParams{
251 : nodeB,
252 : nodeA,
253 84 : edgeInfo.localBPorts,
254 84 : edgeInfo.localAPorts,
255 : edgeInfo.position,
256 : edgeInfo.topoType,
257 84 : edgeInfo.topoInstId,
258 84 : edgeInfo.protocols};
259 84 : auto baLinks = CreateLink(edgeInfo.linkType, baLinkParams);
260 84 : graph->AddEdge(nodeBId, nodeAId, baLinks);
261 84 : }
262 :
263 21 : return graph;
264 0 : }
265 :
266 11 : void PhyTopoBuilder::RecoverBuild(const TopoInfo& topoInfo)
267 : {
268 11 : std::lock_guard<std::mutex> lock(phyTopoMutex);
269 :
270 : // PhyTopo::GetInstance()->ResetInstance();
271 11 : if (PhyTopo::GetInstance()->IsInitFinished()) {
272 1 : return;
273 : }
274 : // 根据topoInfo,按netLayer构造Graph
275 21 : for (const auto& iter : topoInfo.edges) {
276 11 : auto netLayer = iter.first;
277 11 : auto graph = CreateGraph(iter.second);
278 11 : PhyTopo::GetInstance()->AddTopoGraph(netLayer, graph);
279 11 : HCCL_DEBUG("[PhyTopoBuilder::%s]Build netLayer[%u] topo graph success.", __func__, netLayer);
280 11 : }
281 :
282 10 : PhyTopo::GetInstance()->InitFinish();
283 :
284 10 : HCCL_DEBUG("[PhyTopoBuilder::%s]build physic topo success.", __func__);
285 10 : PhyTopo::GetInstance()->Dump();
286 11 : }
287 :
288 15 : std::shared_ptr<TopoInfo> PhyTopoBuilder::GetTopoInfo() const { return topoInfo_; }
289 :
290 : } // namespace Hccl
|