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