LCOV - code coverage report
Current view: top level - legacy/ascend950/framework/topo/topo_addr_info/src - npu_nic_affinity.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 86.5 % 304 263
Test Date: 2026-08-18 17:47:01 Functions: 100.0 % 20 20

            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              : }
        

Generated by: LCOV version 2.0-1