Line data Source code
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 "npu_nic_affinity.h"
12 : #include "xml_parser.h"
13 :
14 : #include <arpa/inet.h>
15 : #include <dirent.h>
16 : #include <ifaddrs.h>
17 : #include <stdlib.h>
18 : #include <string.h>
19 : #include <stdbool.h>
20 : #include <sys/socket.h>
21 :
22 : #include "hal.h"
23 : #include "securec.h"
24 : #include "topo_addr_info_log.h"
25 : #include "topo_addr_info_perf.h"
26 :
27 : /* ───────── 常量 ───────── */
28 : #ifndef XML_PATH
29 : #define XML_PATH "/var/run/ascend-topologyd/virtualTopology.xml"
30 : #endif
31 : #ifndef HCA_NET_PATH_TEMPLATE
32 : #define HCA_NET_PATH_TEMPLATE "/sys/class/infiniband/%s/device/net"
33 : #endif
34 : #define MAX_PATH_LEN 512
35 : #define MAX_GROUP_CNT 16
36 : #define MAX_HCA_COUNT 64
37 : #define MAX_NAME_LEN 64
38 : #define MAX_IP_STR_LEN 48
39 :
40 : /* ───────── 业务结构 ───────── */
41 : typedef struct {
42 : int npuIds[MAX_NPU_COUNT];
43 : unsigned int npuCnt;
44 : unsigned int nicIdx[MAX_HCA_COUNT];
45 : unsigned int nicCnt;
46 : } AffinityGroup;
47 :
48 : typedef struct {
49 : char nicNames[MAX_HCA_COUNT][MAX_NAME_LEN];
50 : unsigned int nicCount;
51 : AffinityGroup groups[MAX_GROUP_CNT];
52 : unsigned int groupCount;
53 : bool affined[MAX_NPU_COUNT][MAX_HCA_COUNT]; /* groups 展开后的 NPU×NIC 亲和矩阵 */
54 : } XmlInfo;
55 :
56 12728 : static bool TagIs(const TagEntry* e, const char* name) { return strcmp(e->tagName, name) == 0; }
57 :
58 : /* ─── 硬件枚举 ─── */
59 :
60 384 : static void BuildNpuBdfTable(char bdfs[MAX_NPU_COUNT][MAX_NAME_LEN])
61 : {
62 384 : int npuCnt = hal_get_npu_count();
63 384 : if (npuCnt <= 0 || npuCnt > (int)MAX_NPU_COUNT) {
64 0 : TOPO_ERR("BuildNpuBdfTable: invalid npuCnt=%d", npuCnt);
65 0 : return;
66 : }
67 3245 : for (int phyId = 0; phyId < npuCnt; phyId++) {
68 : struct dcmi_pcie_info_all pcieInfo;
69 2861 : if (hal_get_device_pcie_info(phyId, &pcieInfo) == 0) {
70 1018 : (void)sprintf_s(
71 1018 : bdfs[phyId], MAX_NAME_LEN, "%04x:%02x:%02x.%x", pcieInfo.domain, pcieInfo.bdf_busid,
72 : pcieInfo.bdf_deviceid, pcieInfo.bdf_funcid);
73 : } else {
74 1843 : bdfs[phyId][0] = '\0';
75 : }
76 : }
77 : }
78 :
79 : /* ─── 亲和分组构建 ─── */
80 :
81 1256 : static void TryAddNpuByBdf(
82 : const TagEntry* e, unsigned int curGroupIdx, const char npuBdfs[MAX_NPU_COUNT][MAX_NAME_LEN], XmlInfo* info)
83 : {
84 1256 : const char* busId = TagFindAttr(e, "busid");
85 1256 : if (busId == NULL || busId[0] == '\0') {
86 2 : return;
87 : }
88 1254 : if (curGroupIdx >= MAX_GROUP_CNT) {
89 0 : return;
90 : }
91 :
92 : /* 遍历所有 NPU 的 BDF 表,找与 busId 匹配的那个 NPU */
93 1254 : int npuCount = hal_get_npu_count();
94 1254 : unsigned int tableSize = (npuCount <= 0) ? 0 : (unsigned int)npuCount;
95 1254 : if (tableSize > MAX_NPU_COUNT) {
96 0 : tableSize = MAX_NPU_COUNT;
97 : }
98 6489 : for (unsigned int npuIdx = 0; npuIdx < tableSize; npuIdx++) {
99 6253 : if (npuBdfs[npuIdx][0] == '\0') {
100 1058 : continue;
101 : }
102 5195 : if (strcmp(busId, npuBdfs[npuIdx]) != 0) {
103 4177 : continue;
104 : }
105 :
106 : /* busId 匹配 → 将 NPU[npuIdx] 加入亲和组 */
107 1018 : AffinityGroup* group = &info->groups[curGroupIdx];
108 1018 : unsigned int groupCnt = group->npuCnt;
109 1018 : if (groupCnt > MAX_NPU_COUNT) {
110 0 : groupCnt = MAX_NPU_COUNT;
111 : }
112 :
113 : /* 检查该 NPU 是否已在组内,避免重复占用槽位 */
114 1018 : bool alreadyInGroup = false;
115 3594 : for (unsigned int existIdx = 0; existIdx < groupCnt; existIdx++) {
116 2577 : if (group->npuIds[existIdx] == (int)npuIdx) {
117 1 : alreadyInGroup = true;
118 1 : break;
119 : }
120 : }
121 1018 : if (!alreadyInGroup && groupCnt < MAX_NPU_COUNT) {
122 1017 : group->npuIds[groupCnt] = (int)npuIdx;
123 1017 : group->npuCnt = groupCnt + 1;
124 : }
125 1018 : break;
126 : }
127 : }
128 :
129 : /* ─── 分组上下文,降低标签处理器的参数传递 ─── */
130 : typedef struct {
131 : const char (*npuBdfs)[MAX_NAME_LEN];
132 : XmlInfo* info;
133 : unsigned int* curGroupIdx;
134 : unsigned int* groupCount;
135 : unsigned int* nicCount;
136 : bool* inGroup;
137 : int* containerDepth;
138 : } GroupCtx;
139 :
140 670 : static void GroupEnterOrSkip(GroupCtx* ctx, int depth)
141 : {
142 670 : if (*ctx->inGroup && depth <= *ctx->containerDepth) {
143 85 : *ctx->inGroup = false;
144 : }
145 670 : if (!*ctx->inGroup && *ctx->groupCount < MAX_GROUP_CNT) {
146 469 : *ctx->curGroupIdx = *ctx->groupCount;
147 469 : (*ctx->groupCount)++;
148 469 : *ctx->containerDepth = depth;
149 469 : *ctx->inGroup = true;
150 : }
151 670 : }
152 :
153 1256 : static void HandlePciTag(const TagEntry* e, GroupCtx* ctx)
154 : {
155 1256 : if (!e->isSelfClose) {
156 239 : GroupEnterOrSkip(ctx, e->depth);
157 239 : if (*ctx->inGroup) {
158 239 : TryAddNpuByBdf(e, *ctx->curGroupIdx, ctx->npuBdfs, ctx->info);
159 : }
160 239 : return;
161 : }
162 1017 : if (*ctx->inGroup) {
163 1017 : TryAddNpuByBdf(e, *ctx->curGroupIdx, ctx->npuBdfs, ctx->info);
164 : }
165 : }
166 :
167 431 : static void HandleUbTag(const TagEntry* e, GroupCtx* ctx)
168 : {
169 431 : if (!e->isSelfClose) {
170 431 : GroupEnterOrSkip(ctx, e->depth);
171 : }
172 431 : }
173 :
174 986 : static void HandleNpuTag(const TagEntry* e, GroupCtx* ctx)
175 : {
176 986 : if (!*ctx->inGroup) {
177 11 : return;
178 : }
179 986 : const char* chipId = TagFindAttr(e, "chipphyid");
180 986 : if (chipId == NULL) {
181 0 : return;
182 : }
183 986 : int phyId = atoi(chipId);
184 986 : if (phyId < 0 || phyId >= (int)MAX_NPU_COUNT) {
185 0 : return;
186 : }
187 : /* 跳过当前进程不可见的设备 */
188 986 : int userDevId = -1;
189 986 : if (hal_get_userdevid_by_phyid(phyId, &userDevId) != 0) {
190 10 : return;
191 : }
192 976 : XmlInfo* info = ctx->info;
193 976 : if (info == NULL) {
194 0 : return;
195 : }
196 :
197 976 : unsigned int gIdx = *ctx->curGroupIdx;
198 976 : if (gIdx >= MAX_GROUP_CNT) {
199 0 : TOPO_ERR("HandleNpuTag: group index overflow, gIdx=%u >= MAX_GROUP_CNT=%d", gIdx, MAX_GROUP_CNT);
200 0 : return;
201 : }
202 :
203 : /* 去重:同组已有该 phyId 则跳过,防止重复占用 npuIds 槽位 */
204 976 : AffinityGroup* group = &info->groups[gIdx];
205 976 : unsigned int groupNpuCnt = group->npuCnt;
206 976 : if (groupNpuCnt > MAX_NPU_COUNT) {
207 0 : groupNpuCnt = MAX_NPU_COUNT;
208 : }
209 3478 : for (unsigned int existIdx = 0; existIdx < groupNpuCnt; existIdx++) {
210 2503 : if (group->npuIds[existIdx] == phyId) {
211 1 : return;
212 : }
213 : }
214 975 : if (groupNpuCnt < MAX_NPU_COUNT) {
215 975 : group->npuIds[groupNpuCnt] = phyId;
216 975 : group->npuCnt = groupNpuCnt + 1;
217 : }
218 : }
219 :
220 : /* 将 NIC 名去重加入 info,返回 TOPO_SUCCESS 并通过 nicIdx 输出索引 */
221 563 : static TopoAddrResult DedupNetNic(XmlInfo* info, const char* name, unsigned int* nicCount, unsigned int* nicIdx)
222 : {
223 563 : unsigned int curCnt = *nicCount;
224 563 : if (curCnt > MAX_HCA_COUNT) {
225 0 : curCnt = MAX_HCA_COUNT;
226 : }
227 563 : unsigned int idx = 0;
228 1148 : for (; idx < curCnt; idx++) {
229 665 : if (strcmp(info->nicNames[idx], name) == 0) {
230 80 : *nicIdx = idx;
231 80 : return TOPO_SUCCESS;
232 : }
233 : }
234 483 : if (idx >= MAX_HCA_COUNT) {
235 0 : TOPO_ERR("DedupNetNic: NIC count overflow, name=%s", name);
236 0 : return TOPO_ERR_INTERNAL;
237 : }
238 483 : if (strcpy_s(info->nicNames[idx], sizeof(info->nicNames[0]), name) != 0) {
239 0 : return TOPO_ERR_INTERNAL;
240 : }
241 483 : (*nicCount)++;
242 483 : *nicIdx = idx;
243 483 : return TOPO_SUCCESS;
244 : }
245 :
246 563 : static void HandleNetTag(const TagEntry* e, GroupCtx* ctx)
247 : {
248 563 : if (!*ctx->inGroup) {
249 0 : return;
250 : }
251 563 : const char* name = TagFindAttr(e, "name");
252 563 : if (name == NULL) {
253 0 : return;
254 : }
255 563 : XmlInfo* info = ctx->info;
256 563 : if (info == NULL) {
257 0 : return;
258 : }
259 :
260 : unsigned int nicIdx;
261 563 : if (DedupNetNic(info, name, ctx->nicCount, &nicIdx) != TOPO_SUCCESS) {
262 0 : return;
263 : }
264 :
265 563 : unsigned int gIdx = *ctx->curGroupIdx;
266 563 : if (gIdx >= MAX_GROUP_CNT) {
267 0 : TOPO_ERR("HandleNetTag: group index overflow, gIdx=%u >= MAX_GROUP_CNT=%d", gIdx, MAX_GROUP_CNT);
268 0 : return;
269 : }
270 563 : AffinityGroup* group = &info->groups[gIdx];
271 : /* 组内去重:同名 NIC 只加入一次,避免重复占用 nicIdx 槽位 */
272 563 : unsigned int groupNicCnt = group->nicCnt;
273 563 : if (groupNicCnt > MAX_HCA_COUNT) {
274 0 : groupNicCnt = MAX_HCA_COUNT;
275 : }
276 660 : for (unsigned int existIdx = 0; existIdx < groupNicCnt; existIdx++) {
277 97 : if (group->nicIdx[existIdx] == nicIdx) {
278 0 : return;
279 : }
280 : }
281 563 : if (groupNicCnt < MAX_HCA_COUNT) {
282 563 : group->nicIdx[groupNicCnt] = nicIdx;
283 563 : group->nicCnt = groupNicCnt + 1;
284 : }
285 : }
286 :
287 : /* 将 AffinityGroup 展开为 affined 二维矩阵,供 O(1) 亲和查询 */
288 384 : static void BuildAffinityMatrix(XmlInfo* info)
289 : {
290 384 : TOPO_PERF_BEGIN(BuildAffinityMatrix);
291 384 : unsigned int groupCnt = info->groupCount;
292 384 : if (groupCnt > MAX_GROUP_CNT) {
293 0 : groupCnt = MAX_GROUP_CNT;
294 : }
295 :
296 853 : for (unsigned int g = 0; g < groupCnt; g++) {
297 469 : unsigned int npuCnt = info->groups[g].npuCnt;
298 469 : if (npuCnt > MAX_NPU_COUNT) {
299 0 : npuCnt = MAX_NPU_COUNT;
300 : }
301 469 : unsigned int nicCnt = info->groups[g].nicCnt;
302 469 : if (nicCnt > MAX_HCA_COUNT) {
303 0 : nicCnt = MAX_HCA_COUNT;
304 : }
305 2461 : for (unsigned int ni = 0; ni < npuCnt; ni++) {
306 1992 : int npuId = info->groups[g].npuIds[ni];
307 1992 : if (npuId < 0 || npuId >= (int)MAX_NPU_COUNT) {
308 0 : continue;
309 : }
310 4110 : for (unsigned int nci = 0; nci < nicCnt; nci++) {
311 2118 : unsigned int nicIdx = info->groups[g].nicIdx[nci];
312 2118 : if (nicIdx < MAX_HCA_COUNT) {
313 2118 : info->affined[(unsigned int)npuId][nicIdx] = true;
314 : }
315 : }
316 : }
317 : }
318 384 : TOPO_PERF_END(BuildAffinityMatrix);
319 384 : }
320 :
321 384 : static TopoAddrResult BuildAffinityGroups(const TagEntry* tags, unsigned int tagCount, XmlInfo* info)
322 : {
323 384 : char npuBdfs[MAX_NPU_COUNT][MAX_NAME_LEN] = {{0}};
324 384 : BuildNpuBdfTable(npuBdfs);
325 384 : (void)memset_s(info, sizeof(*info), 0, sizeof(*info));
326 :
327 384 : unsigned int curGroupIdx = 0;
328 384 : unsigned int groupCount = 0;
329 384 : unsigned int nicCount = 0;
330 384 : bool inGroup = false;
331 384 : int containerDepth = -1;
332 :
333 384 : GroupCtx ctx = {
334 : .npuBdfs = (const char(*)[MAX_NAME_LEN])npuBdfs,
335 : .info = info,
336 : .curGroupIdx = &curGroupIdx,
337 : .groupCount = &groupCount,
338 : .nicCount = &nicCount,
339 : .inGroup = &inGroup,
340 : .containerDepth = &containerDepth,
341 : };
342 :
343 4970 : for (unsigned int i = 0; i < tagCount; i++) {
344 4586 : const TagEntry* e = &tags[i];
345 :
346 4586 : if (TagIs(e, "pci")) {
347 1256 : HandlePciTag(e, &ctx);
348 1256 : continue;
349 : }
350 3330 : if (TagIs(e, "ub")) {
351 431 : HandleUbTag(e, &ctx);
352 431 : continue;
353 : }
354 2899 : if (TagIs(e, "npu")) {
355 986 : HandleNpuTag(e, &ctx);
356 986 : continue;
357 : }
358 1913 : if (TagIs(e, "net")) {
359 563 : HandleNetTag(e, &ctx);
360 563 : continue;
361 : }
362 : }
363 :
364 384 : info->groupCount = groupCount;
365 384 : info->nicCount = nicCount;
366 :
367 384 : BuildAffinityMatrix(info);
368 :
369 384 : if (nicCount == 0) {
370 3 : TOPO_ERR("no NICs found in XML, cannot build affinity groups");
371 3 : return TOPO_ERR_NOT_FOUND;
372 : }
373 381 : return TOPO_SUCCESS;
374 : }
375 :
376 : /* ─── 合成:ParseXml = ParseXmlTags + BuildAffinityGroups ─── */
377 :
378 396 : static TopoAddrResult ParseXml(XmlInfo* info)
379 : {
380 : TagEntry tags[MAX_TAG_ENTRIES];
381 396 : unsigned int tagCount = 0;
382 396 : TopoAddrResult ret = ParseXmlTags(XML_PATH, tags, &tagCount, MAX_TAG_ENTRIES);
383 396 : if (ret != TOPO_SUCCESS) {
384 12 : return ret;
385 : }
386 384 : return BuildAffinityGroups(tags, tagCount, info);
387 : }
388 :
389 : /* ─── 打印 NPU → 网卡名 → IP 分配结果 ─── */
390 381 : static void LogAssignResult(
391 : const XmlInfo* info, int npuCount, const bool nicValid[MAX_HCA_COUNT], const char nicIps[][MAX_IP_STR_LEN],
392 : const char assignment[][MAX_IP_STR_LEN])
393 : {
394 3238 : for (int ni = 0; ni < npuCount; ni++) {
395 2857 : if (assignment[ni][0] == '\0') {
396 876 : continue;
397 : }
398 : /* 反向查找该 IP 对应的 NIC 名 */
399 1981 : const char* nicName = NULL;
400 2334 : for (unsigned int j = 0; j < info->nicCount; j++) {
401 2334 : if (nicValid[j] && strcmp(assignment[ni], nicIps[j]) == 0) {
402 1981 : nicName = info->nicNames[j];
403 1981 : break;
404 : }
405 : }
406 1981 : TOPO_INFO("NPU %d → %s (%s)", ni, assignment[ni], nicName ? nicName : "?");
407 : }
408 381 : }
409 :
410 : /* ─── 轮询分发全量 NPU 的 RoCE IP ─── */
411 :
412 381 : static TopoAddrResult DispatchIpsRoundRobin(
413 : int phyId, const XmlInfo* info, const bool nicValid[MAX_HCA_COUNT], const char nicIps[][MAX_IP_STR_LEN],
414 : char* outIp, size_t outLen)
415 : {
416 381 : if (phyId < 0 || phyId >= (int)MAX_NPU_COUNT) {
417 0 : TOPO_ERR("DispatchIpsRoundRobin: invalid phyId=%d", phyId);
418 0 : return TOPO_ERR_PARA;
419 : }
420 381 : int npuCount = hal_get_npu_count();
421 381 : if (npuCount <= 0 || npuCount > (int)MAX_NPU_COUNT) {
422 0 : TOPO_ERR("DispatchIpsRoundRobin: invalid npuCount=%d", npuCount);
423 0 : return TOPO_ERR_INTERNAL;
424 : }
425 381 : unsigned int nicCount = info->nicCount;
426 381 : if (nicCount == 0 || nicCount > MAX_HCA_COUNT) {
427 0 : TOPO_ERR("DispatchIpsRoundRobin: invalid nicCount=%u", nicCount);
428 0 : return TOPO_ERR_INTERNAL;
429 : }
430 :
431 : char assignment[MAX_NPU_COUNT][MAX_IP_STR_LEN];
432 381 : (void)memset_s(assignment, sizeof(assignment), 0, sizeof(assignment));
433 :
434 381 : unsigned int cur = 0;
435 3238 : for (int npuId = 0; npuId < npuCount; npuId++) {
436 3802 : for (unsigned int j = cur; j < cur + nicCount; j++) {
437 2926 : unsigned int nicIdx = j % nicCount;
438 2926 : if (!nicValid[nicIdx] || !info->affined[npuId][nicIdx]) {
439 945 : continue;
440 : }
441 1981 : if (strcpy_s(assignment[npuId], sizeof(assignment[0]), nicIps[nicIdx]) != 0) {
442 0 : continue;
443 : }
444 1981 : cur = (j + 1) % nicCount;
445 1981 : break;
446 : }
447 : }
448 :
449 381 : LogAssignResult(info, npuCount, nicValid, nicIps, assignment);
450 :
451 381 : if (assignment[phyId][0] != '\0') {
452 265 : return strcpy_s(outIp, outLen, assignment[phyId]);
453 : }
454 116 : TOPO_ERR("no IP assigned for phyId=%d", phyId);
455 116 : return TOPO_ERR_NOT_FOUND;
456 : }
457 :
458 : /* ─── 名称 → IP 转换 ─── */
459 :
460 : /* 直接以 eth 名查 IP */
461 845 : static TopoAddrResult EthToIp(const char* eth, char* ip, size_t ipLen)
462 : {
463 845 : struct ifaddrs* ifaddr = NULL;
464 845 : if (getifaddrs(&ifaddr) == -1) {
465 0 : return TOPO_ERR_SYSCALL;
466 : }
467 :
468 1557 : for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
469 1188 : if (ifa->ifa_addr == NULL || ifa->ifa_name == NULL) {
470 1 : continue;
471 : }
472 1187 : if (strcmp(ifa->ifa_name, eth) != 0) {
473 710 : continue;
474 : }
475 477 : if (ifa->ifa_addr->sa_family != AF_INET) {
476 1 : continue;
477 : }
478 476 : struct sockaddr_in* sin = (struct sockaddr_in*)ifa->ifa_addr;
479 476 : if (inet_ntop(AF_INET, &sin->sin_addr, ip, (socklen_t)ipLen) != NULL) {
480 476 : freeifaddrs(ifaddr);
481 476 : return TOPO_SUCCESS;
482 : }
483 : }
484 369 : freeifaddrs(ifaddr);
485 369 : return TOPO_ERR_NOT_FOUND;
486 : }
487 :
488 : /* 以 HCA 名查 IP:/sys/class/infiniband/<hca>/device/net/<eth> */
489 368 : static TopoAddrResult HcaToIp(const char* hca, char* ip, size_t ipLen)
490 : {
491 : char netPath[MAX_PATH_LEN];
492 368 : if (sprintf_s(netPath, sizeof(netPath), HCA_NET_PATH_TEMPLATE, hca) < 0) {
493 0 : return TOPO_ERR_INTERNAL;
494 : }
495 :
496 368 : DIR* dir = opendir(netPath);
497 368 : if (dir == NULL) {
498 5 : return TOPO_ERR_NOT_FOUND;
499 : }
500 :
501 363 : char eth[MAX_NAME_LEN] = {0};
502 363 : struct dirent* entry = NULL;
503 728 : while ((entry = readdir(dir)) != NULL) {
504 727 : if (entry->d_name[0] == '.') {
505 365 : continue;
506 : }
507 362 : (void)strncpy_s(eth, sizeof(eth), entry->d_name, sizeof(eth) - 1);
508 362 : break;
509 : }
510 363 : closedir(dir);
511 :
512 363 : if (eth[0] == '\0') {
513 1 : return TOPO_ERR_NOT_FOUND;
514 : }
515 :
516 362 : return EthToIp(eth, ip, ipLen);
517 : }
518 :
519 : /* name 可能是 eth 名或 HCA 名:先尝试 eth,再尝试 HCA */
520 483 : static TopoAddrResult NameToIp(const char* name, char* ip, size_t ipLen)
521 : {
522 483 : if (EthToIp(name, ip, ipLen) == TOPO_SUCCESS) {
523 115 : return TOPO_SUCCESS;
524 : }
525 368 : TopoAddrResult ret = HcaToIp(name, ip, ipLen);
526 368 : if (ret != TOPO_SUCCESS) {
527 7 : TOPO_ERR("NameToIp: cannot resolve IP for %s (eth+HCA)", name);
528 : }
529 368 : return ret;
530 : }
531 :
532 : /* ─── 打印 NPU-NIC 亲和关系(NIC 名 + IP) ─── */
533 :
534 381 : static void LogAffinityInfo(const XmlInfo* info, const char nicIps[][MAX_IP_STR_LEN])
535 : {
536 381 : unsigned int nicCnt = info->nicCount;
537 381 : if (nicCnt > MAX_HCA_COUNT) {
538 0 : nicCnt = MAX_HCA_COUNT;
539 : }
540 381 : int npuCount = hal_get_npu_count();
541 381 : if (npuCount <= 0 || npuCount > (int)MAX_NPU_COUNT) {
542 0 : return;
543 : }
544 3238 : for (int npuId = 0; npuId < npuCount; npuId++) {
545 6539 : for (unsigned int nicIdx = 0; nicIdx < nicCnt; nicIdx++) {
546 3682 : if (info->affined[npuId][nicIdx]) {
547 2118 : TOPO_INFO(
548 : "[affinity] NPU%d <- %s(%s)", npuId, info->nicNames[nicIdx],
549 : (nicIps[nicIdx][0] != '\0') ? nicIps[nicIdx] : "?");
550 : }
551 : }
552 : }
553 : }
554 :
555 : /* ─── 预解析全部 NIC 的 IP → 调用轮询分发 → 直接出 IP ─── */
556 :
557 381 : static TopoAddrResult SelectNpuRoceIp(int npuId, const XmlInfo* info, char* ip, size_t ipLen)
558 : {
559 381 : unsigned int nicCnt = info->nicCount;
560 381 : if (nicCnt > MAX_HCA_COUNT) {
561 0 : nicCnt = MAX_HCA_COUNT;
562 : }
563 :
564 381 : char nicIps[MAX_HCA_COUNT][MAX_IP_STR_LEN] = {{0}};
565 381 : bool nicValid[MAX_HCA_COUNT] = {false};
566 864 : for (unsigned int i = 0; i < nicCnt; i++) {
567 483 : if (NameToIp(info->nicNames[i], nicIps[i], sizeof(nicIps[i])) == TOPO_SUCCESS) {
568 476 : nicValid[i] = true;
569 : }
570 : }
571 381 : LogAffinityInfo(info, nicIps);
572 381 : return DispatchIpsRoundRobin(npuId, info, nicValid, nicIps, ip, ipLen);
573 : }
574 :
575 : /* ─── 对外接口 ─── */
576 :
577 398 : TopoAddrResult GetRoceIpFromXml(int npuId, char* ip, size_t ipLen)
578 : {
579 398 : if (ip == NULL || ipLen == 0 || npuId < 0) {
580 2 : TOPO_ERR("GetRoceIpFromXml: invalid params (ip=%p, ipLen=%zu, npuId=%d)", ip, ipLen, npuId);
581 2 : return TOPO_ERR_PARA;
582 : }
583 :
584 : /* 解析 XML,构建 NPU-NIC 亲和分组 */
585 : XmlInfo info;
586 396 : (void)memset_s(&info, sizeof(info), 0, sizeof(info));
587 396 : TopoAddrResult ret = ParseXml(&info);
588 396 : if (ret != TOPO_SUCCESS) {
589 15 : return ret;
590 : }
591 :
592 : /* XML 中无 NIC 定义 */
593 381 : if (info.nicCount == 0) {
594 0 : TOPO_ERR("GetRoceIpFromXml: no NICs in XML for npuId=%d", npuId);
595 0 : return TOPO_ERR_NOT_FOUND;
596 : }
597 :
598 : /* 从所有 NIC 中轮询选出当前 NPU 的 RoCE IP */
599 381 : return SelectNpuRoceIp(npuId, &info, ip, ipLen);
600 : }
|