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 56 : PhyTopoBuilder& PhyTopoBuilder::GetInstance()
22 : {
23 56 : static PhyTopoBuilder phyTopoBuilder;
24 56 : return phyTopoBuilder;
25 : }
26 :
27 : // 根据nodeType和localId创建节点
28 73 : std::shared_ptr<PhyTopo::Node> CreateNode(const PhyTopo::Node::NodeType nodeType, const LocalId localId)
29 : {
30 73 : if (nodeType == PhyTopo::Node::NodeType::PEER) {
31 56 : return std::make_shared<PhyTopo::Peer>(localId);
32 : } else {
33 17 : return std::make_shared<PhyTopo::Fabric>();
34 : }
35 : }
36 :
37 17 : void PhyTopoBuilder::Build(const std::string& topoPath)
38 : {
39 17 : std::lock_guard<std::mutex> lock(phyTopoMutex);
40 :
41 17 : 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 6 : 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 18 : HCCL_DEBUG("[PhyTopoBuilder::%s]Start to build physic topo.", __func__);
57 :
58 6 : auto topoInfo = LoadTopoInfo(topoPath);
59 6 : BuildPhyTopo(*topoInfo);
60 :
61 6 : PhyTopo::GetInstance()->InitFinish();
62 :
63 18 : HCCL_DEBUG("[PhyTopoBuilder::%s]build physic topo success.", __func__);
64 6 : PhyTopo::GetInstance()->Dump();
65 17 : }
66 :
67 9 : std::shared_ptr<TopoInfo> PhyTopoBuilder::LoadTopoInfo(const std::string& topoPath)
68 : {
69 9 : std::shared_ptr<TopoInfo> topoInfo = std::make_shared<TopoInfo>();
70 : // 检查是否为非法路径以及size的大小。
71 : struct stat fileStat;
72 9 : if (stat(topoPath.c_str(), &fileStat) != 0) {
73 2 : HCCL_ERROR(
74 : "[PhyTopoBuilder][LoadTopoInfo] Get file stat failed, file path:%s, errno:%d, error: %s", topoPath.c_str(),
75 : errno, strerror(errno));
76 2 : THROW<InvalidParamsException>(
77 : "[PhyTopoBuilder][LoadTopoInfo]Get file stat failed, file path:%s, errno:%d, error: %s", topoPath.c_str(),
78 2 : errno, strerror(errno));
79 : }
80 :
81 7 : u64 topoFileSize = static_cast<u64>(fileStat.st_size);
82 7 : if (topoFileSize > SUPPORT_MAX_TOPOFILE_SIZE || topoFileSize <= 0) {
83 1 : HCCL_ERROR(
84 : "[PhyTopoBuilder][LoadTopoInfo] topoFileSize size: %llu, topoFile must be greater than 0 and less than %u",
85 : topoFileSize, SUPPORT_MAX_TOPOFILE_SIZE);
86 2 : THROW<InvalidParamsException>(StringFormat(
87 : "[PhyTopoBuilder][LoadTopoInfo]file %s size (%llu bytes) exceeds max allowed size (%u bytes)",
88 : topoPath.c_str(), topoFileSize, SUPPORT_MAX_TOPOFILE_SIZE));
89 : }
90 :
91 : JsonParser topoParser;
92 6 : topoParser.ParseFile(topoPath, *topoInfo);
93 6 : topoInfo_ = topoInfo;
94 6 : return topoInfo;
95 3 : }
96 :
97 64 : shared_ptr<PhyTopo::Link> CreatePeer2PeerLink(const LinkParams& params)
98 : {
99 64 : LinkType linkType = LinkType::PEER2PEER;
100 : auto srcIface
101 64 : = std::make_shared<PhyTopo::ConnInterface>(params.localAPorts, params.position, linkType, params.protocols);
102 : auto dstIface
103 64 : = std::make_shared<PhyTopo::ConnInterface>(params.localBPorts, params.position, linkType, params.protocols);
104 64 : PhyTopo::LinkAttributes linkAttrs;
105 64 : linkAttrs.linktype = linkType;
106 64 : linkAttrs.protocols = params.protocols;
107 : auto link = std::make_shared<PhyTopo::Link>(
108 64 : params.srcNode, params.dstNode, linkAttrs, params.topoType, params.topoInstanceId);
109 64 : link->SetSourceIface(srcIface);
110 64 : params.srcNode->AddConnInterface(srcIface);
111 64 : link->SetTargetIface(dstIface);
112 64 : params.dstNode->AddConnInterface(dstIface);
113 128 : return {link};
114 64 : }
115 :
116 186 : shared_ptr<PhyTopo::Link> CreatePeer2NetLink(const LinkParams& params)
117 : {
118 186 : LinkType linkType = LinkType::PEER2NET;
119 : auto srcIface
120 186 : = std::make_shared<PhyTopo::ConnInterface>(params.localAPorts, params.position, linkType, params.protocols);
121 : // fabric 没有ports
122 186 : std::set<std::string> ports{};
123 186 : auto dstIface = std::make_shared<PhyTopo::ConnInterface>(ports, params.position, linkType, params.protocols);
124 186 : PhyTopo::LinkAttributes linkAttrs;
125 186 : linkAttrs.linktype = linkType;
126 186 : linkAttrs.protocols = params.protocols;
127 : auto link = std::make_shared<PhyTopo::Link>(
128 186 : params.srcNode, params.dstNode, linkAttrs, params.topoType, params.topoInstanceId);
129 186 : link->SetSourceIface(srcIface);
130 186 : params.srcNode->AddConnInterface(srcIface);
131 186 : link->SetTargetIface(dstIface);
132 186 : params.dstNode->AddConnInterface(dstIface);
133 372 : return {link};
134 186 : }
135 :
136 : // 根据链路类型建链
137 250 : shared_ptr<PhyTopo::Link> CreateLink(const LinkType linkType, const LinkParams& params)
138 : {
139 250 : if (linkType == LinkType::PEER2PEER) {
140 64 : return CreatePeer2PeerLink(params);
141 : } else {
142 186 : return CreatePeer2NetLink(params);
143 : }
144 : }
145 :
146 250 : NodeId GetNodeId(const PhyTopo::Node::NodeType nodeType, LocalId localId)
147 : {
148 250 : switch (nodeType) {
149 157 : case PhyTopo::Node::NodeType::PEER:
150 157 : return PhyTopo::Peer::GetId(localId);
151 93 : case PhyTopo::Node::NodeType::FABRIC:
152 93 : return PhyTopo::Fabric::GetId();
153 0 : default:
154 0 : THROW<InvalidParamsException>(StringFormat("[PhyTopoBuilder]Invalid NodeType."));
155 : return 0;
156 : }
157 : }
158 :
159 : std::shared_ptr<Graph<PhyTopo::Node, PhyTopo::Link>>
160 25 : PhyTopoBuilder::CreateGraph(const std::vector<EdgeInfo>& edges) const
161 : {
162 : // 所有物理边构建到同一张图,逻辑分层由 RankTable 端口确定。
163 25 : auto graph = std::make_shared<Graph<PhyTopo::Node, PhyTopo::Link>>();
164 :
165 150 : for (const auto& edgeInfo : edges) {
166 125 : std::shared_ptr<PhyTopo::Node> nodeA;
167 125 : std::shared_ptr<PhyTopo::Node> nodeB;
168 :
169 : // 生成 nodeAId,localA 始终为 PEER 类型
170 125 : NodeId nodeAId = GetNodeId(PhyTopo::Node::NodeType::PEER, edgeInfo.localA);
171 : // 获取或创建 nodeA
172 125 : if (!graph->HasNode(nodeAId)) {
173 22 : nodeA = CreateNode(PhyTopo::Node::NodeType::PEER, edgeInfo.localA);
174 22 : graph->AddNode(nodeAId, nodeA);
175 36 : HCCL_DEBUG("[PhyTopoBuilder::%s] Add node[%llu] success.", __func__, nodeAId);
176 : } else {
177 : // 使用 TraverseNode 查找 nodeA
178 103 : graph->TraverseNode([&nodeA, nodeAId](NodeId id, const std::shared_ptr<PhyTopo::Node>& n) {
179 368 : if (id == nodeAId) {
180 103 : nodeA = n;
181 : }
182 368 : });
183 103 : if (nodeA == nullptr) {
184 0 : HCCL_ERROR("[PhyTopoBuilder::CreateGraph] nodeAId [%llu] not found.", nodeAId);
185 0 : THROW<NullPtrException>(
186 0 : StringFormat("[PhyTopoBuilder::CreateGraph] Unable to find node [%llu].", nodeAId));
187 : }
188 : }
189 :
190 : // 根据 linkType 生成 nodeBId
191 125 : PhyTopo::Node::NodeType nodeBType;
192 125 : if (edgeInfo.linkType == LinkType::PEER2PEER) {
193 32 : nodeBType = PhyTopo::Node::NodeType::PEER;
194 : } else {
195 93 : nodeBType = PhyTopo::Node::NodeType::FABRIC;
196 : }
197 :
198 125 : NodeId nodeBId = GetNodeId(nodeBType, edgeInfo.localB);
199 : // 获取或创建 nodeB
200 125 : if (!graph->HasNode(nodeBId)) {
201 42 : nodeB = CreateNode(nodeBType, edgeInfo.localB);
202 42 : graph->AddNode(nodeBId, nodeB);
203 76 : HCCL_DEBUG("[PhyTopoBuilder::%s] Add node[%llu] success.", __func__, nodeBId);
204 : } else {
205 : // 使用 TraverseNode 查找 nodeB
206 83 : graph->TraverseNode([&nodeB, nodeBId](NodeId id, const std::shared_ptr<PhyTopo::Node>& n) {
207 321 : if (id == nodeBId) {
208 83 : nodeB = n;
209 : }
210 321 : });
211 83 : if (nodeB == nullptr) {
212 0 : HCCL_ERROR("[PhyTopoBuilder::CreateGraph] nodeBId [%llu] not found.", nodeBId);
213 0 : THROW<NullPtrException>(
214 0 : StringFormat("[PhyTopoBuilder::CreateGraph] Unable to find node [%llu].", nodeBId));
215 : }
216 : }
217 :
218 : // 双向分别创建link
219 : LinkParams abLinkParams{
220 : nodeA,
221 : nodeB,
222 125 : edgeInfo.localAPorts,
223 125 : edgeInfo.localBPorts,
224 : edgeInfo.position,
225 : edgeInfo.topoType,
226 125 : edgeInfo.topoInstId,
227 125 : edgeInfo.protocols};
228 125 : auto abLinks = CreateLink(edgeInfo.linkType, abLinkParams);
229 125 : graph->AddEdge(nodeAId, nodeBId, abLinks);
230 : LinkParams baLinkParams{
231 : nodeB,
232 : nodeA,
233 125 : edgeInfo.localBPorts,
234 125 : edgeInfo.localAPorts,
235 : edgeInfo.position,
236 : edgeInfo.topoType,
237 125 : edgeInfo.topoInstId,
238 125 : edgeInfo.protocols};
239 125 : auto baLinks = CreateLink(edgeInfo.linkType, baLinkParams);
240 125 : graph->AddEdge(nodeBId, nodeAId, baLinks);
241 125 : }
242 :
243 25 : return graph;
244 0 : }
245 :
246 22 : void PhyTopoBuilder::BuildPhyTopo(const TopoInfo& topoInfo) const
247 : {
248 : // topo.json 只描述物理连接,不再划分逻辑层。
249 22 : auto graph = CreateGraph(topoInfo.edges);
250 : // 补齐未出现在 edge_list 中的 Peer 节点。
251 70 : for (const auto& peerInfo : topoInfo.peers) {
252 48 : NodeId nodeId = PhyTopo::Peer::GetId(peerInfo.localId);
253 48 : if (!graph->HasNode(nodeId)) {
254 9 : auto node = CreateNode(PhyTopo::Node::NodeType::PEER, peerInfo.localId);
255 9 : graph->AddNode(nodeId, node);
256 9 : }
257 : }
258 22 : PhyTopo::GetInstance()->AddTopoGraph(graph);
259 34 : HCCL_DEBUG("[PhyTopoBuilder::%s] Build physical topo graph success.", __func__);
260 22 : }
261 :
262 16 : void PhyTopoBuilder::RecoverBuild(const TopoInfo& topoInfo)
263 : {
264 16 : std::lock_guard<std::mutex> lock(phyTopoMutex);
265 :
266 : // PhyTopo::GetInstance()->ResetInstance();
267 16 : if (PhyTopo::GetInstance()->IsInitFinished()) {
268 1 : return;
269 : }
270 15 : topoInfo_ = std::make_shared<TopoInfo>(topoInfo);
271 15 : BuildPhyTopo(topoInfo);
272 :
273 15 : PhyTopo::GetInstance()->InitFinish();
274 :
275 15 : HCCL_DEBUG("[PhyTopoBuilder::%s]build physic topo success.", __func__);
276 15 : PhyTopo::GetInstance()->Dump();
277 16 : }
278 :
279 16 : std::shared_ptr<TopoInfo> PhyTopoBuilder::GetTopoInfo() const { return topoInfo_; }
280 :
281 : } // namespace Hccl
|