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