LCOV - code coverage report
Current view: top level - legacy/ascend950/framework/topo/rank_info_detect - preempt_port_manager.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 93.5 % 92 86
Test Date: 2026-08-18 17:47:01 Functions: 100.0 % 9 9

            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 "preempt_port_manager_v2.h"
      12              : #include <sstream>
      13              : #include "orion_adapter_hccp.h"
      14              : #include "hccl_common_v2.h"
      15              : #include "adapter_error_manager_pub.h"
      16              : 
      17              : namespace Hccl {
      18              : 
      19              : constexpr s32 HOST_DEVICE_ID = -1; // device id 无效值
      20              : 
      21              : bool PreemptPortManager::initialized = false;
      22              : 
      23           65 : PreemptPortManager::PreemptPortManager()
      24              : {
      25              :     // 根据host or device nic区分
      26           65 :     IpPortRef hostPortRef;
      27           65 :     preemptSockets_.emplace(HrtNetworkMode::PEER, hostPortRef);
      28           65 :     IpPortRef devPortRef;
      29           65 :     preemptSockets_.emplace(HrtNetworkMode::HDC, devPortRef);
      30           65 :     initialized = true;
      31           65 : }
      32              : 
      33           65 : PreemptPortManager::~PreemptPortManager()
      34              : {
      35           65 :     preemptSockets_.clear();
      36           65 :     initialized = false;
      37           65 : }
      38              : 
      39           33 : PreemptPortManager& PreemptPortManager::GetInstance(s32 deviceLogicId)
      40              : {
      41           98 :     static PreemptPortManager instance[MAX_MODULE_DEVICE_NUM];
      42           33 :     if (deviceLogicId == HOST_DEVICE_ID) {
      43           30 :         HCCL_INFO("[GetInstance] deviceLogicId[-1] is HOST_DEVICE_ID");
      44           10 :         return instance[0];
      45              :     }
      46           53 :     CHK_PRT_RET(
      47              :         (static_cast<u32>(deviceLogicId) >= MAX_MODULE_DEVICE_NUM || deviceLogicId < 0),
      48              :         HCCL_WARNING("[PreemptPortManager::%s] deviceLogicId[%d] is invalid", __func__, deviceLogicId), instance[0]);
      49              : 
      50           13 :     return instance[deviceLogicId];
      51              : }
      52              : 
      53            1 : void PreemptPortManager::ListenPreempt(
      54              :     const std::shared_ptr<Socket>& listenSocket, const std::vector<SocketPortRange>& portRange, u32& usePort)
      55              : {
      56            1 :     CHK_PRT_RET(
      57              :         !initialized,
      58              :         HCCL_ERROR("[PreemptPortManager::%s] preempt port manager has already been release.", __func__), );
      59              : 
      60            1 :     CHK_SMART_PTR_RET_NULL(listenSocket);
      61            1 :     NicType nicType = listenSocket->GetNicType();
      62            1 :     HrtNetworkMode netMode = nicType == NicType::HOST_NIC_TYPE ? HrtNetworkMode::PEER : HrtNetworkMode::HDC;
      63            1 :     std::lock_guard<std::mutex> lock(preemptMutex_);
      64            1 :     PreemptPortInRange(listenSocket, netMode, portRange, usePort);
      65            3 :     HCCL_INFO("[PreemptPortManager::%s] listening on port[%u] for nicType[%u] success.", __func__, usePort, nicType);
      66            1 : }
      67              : 
      68            1 : void PreemptPortManager::Release(const std::shared_ptr<Socket>& listenSocket)
      69              : {
      70            1 :     CHK_PRT_RET(
      71              :         !initialized,
      72              :         HCCL_WARNING("[PreemptPortManager::%s] preempt port manager has already been release.", __func__), );
      73              : 
      74            1 :     CHK_SMART_PTR_RET_NULL(listenSocket);
      75            1 :     NicType nicType = listenSocket->GetNicType();
      76            1 :     HrtNetworkMode netMode = nicType == NicType::HOST_NIC_TYPE ? HrtNetworkMode::PEER : HrtNetworkMode::HDC;
      77              : 
      78            1 :     std::lock_guard<std::mutex> lock(preemptMutex_);
      79            1 :     ReleasePreempt(preemptSockets_[netMode], listenSocket, netMode);
      80            3 :     HCCL_INFO("[PreemptPortManager::%s] release socket of type[%u] success.", __func__, nicType);
      81            1 : }
      82              : 
      83            2 : void PreemptPortManager::PreemptPortInRange(
      84              :     const std::shared_ptr<Socket>& listenSocket, HrtNetworkMode netMode, const std::vector<SocketPortRange>& portRange,
      85              :     u32& usePort)
      86              : {
      87            2 :     IpPortRef& portRef = preemptSockets_[netMode];
      88            2 :     std::string ipAddr(listenSocket->GetLocalIp().GetIpStr());
      89            2 :     if (portRef.find(ipAddr) != portRef.end()) {
      90              :         // 如果在这个IP上已经有已经抢占的port,则复用这个port
      91            1 :         usePort = portRef[ipAddr].first;
      92            1 :         bool ret = listenSocket->Listen(usePort);
      93            1 :         CHK_PRT_THROW(
      94              :             !ret, HCCL_ERROR("[PreemptPortManager::%s] usePort[%u] listen failed.", __func__, usePort),
      95              :             InvalidParamsException, "socket listen failed");
      96            1 :         portRef[ipAddr].second.Ref();
      97            3 :         HCCL_INFO(
      98              :             "[PreemptPortManager::%s] socket has already been listened, ref count[%d].", __func__,
      99              :             portRef[ipAddr].second.Count());
     100            1 :         return;
     101              :     }
     102              :     // 如果这个IP上没有抢占过的port,则轮询输入的端口范围,找到一个可用的端口
     103            2 :     for (auto& range : portRange) {
     104            7 :         for (u32 port = range.min; port <= range.max; ++port) {
     105            6 :             if (listenSocket->Listen(port)) {
     106              :                 // 抢占端口成功,将端口记录到计数器中,并作为出参返回
     107            0 :                 usePort = port;
     108            0 :                 portRef[ipAddr].first = usePort;
     109            0 :                 portRef[ipAddr].second.Ref();
     110            0 :                 HCCL_INFO(
     111              :                     "[PreemptPortManager::%s] listen on ip[%s] and port[%u] success.", __func__, ipAddr.c_str(),
     112              :                     usePort);
     113            0 :                 return;
     114              :             }
     115              : 
     116              :             // 当前端口已被占用,尝试抢占下一个端口
     117           18 :             HCCL_INFO("[PreemptPortManager::%s] could not listen on ip[%s], port[%u].", __func__, ipAddr.c_str(), port);
     118              :         }
     119              :     }
     120              :     // 所有端口范围内的端口都已经被占用,没有可用的端口,抢占监听失败
     121              :     std::string errormessage
     122            1 :         = "The IP address " + ipAddr + " and port " + std::to_string(usePort) + " have already been bound.";
     123            1 :     NicType nicType = listenSocket->GetNicType();
     124            1 :     if (nicType == NicType::HOST_NIC_TYPE) {
     125            0 :         RPT_INPUT_ERR(true, "EI0019", std::vector<std::string>({"reason"}), std::vector<std::string>({errormessage}));
     126              :     } else {
     127            7 :         RPT_INPUT_ERR(true, "EI0020", std::vector<std::string>({"reason"}), std::vector<std::string>({errormessage}));
     128              :     }
     129            1 :     std::string portRangeStr = GetRangeStr(portRange);
     130            3 :     HCCL_ERROR("[PreemptPortManager::%s] Complete polling of socket port range:%s", __func__, portRangeStr.c_str());
     131            3 :     HCCL_ERROR(
     132              :         "[PreemptPortManager::%s] All ports in socket port range are bound already. "
     133              :         "no available port to listen. Please check the ports status, or change the port range to listen on.",
     134              :         __func__);
     135              :     std::string envName
     136            1 :         = nicType == NicType::HOST_NIC_TYPE ? "HCCL_HOST_SOCKET_PORT_RANGE" : "HCCL_NPU_SOCKET_PORT_RANGE";
     137            3 :     HCCL_ERROR(
     138              :         "NOTICE: Users need to make sure ports in %s are available for HCCL."
     139              :         "Please double check whether the port are used by others unexpected process. "
     140              :         "The port ranges size should also be enough when running multi-process HCCL.",
     141              :         envName.c_str());
     142            3 :     HCCL_ERROR("NOTICE: The host port range size is not suggested to be smaller than the process number"
     143              :                " on current rank.");
     144            1 :     THROW<InvalidParamsException>("No available port to listen");
     145            6 : }
     146              : 
     147            3 : void PreemptPortManager::ReleasePreempt(
     148              :     IpPortRef& portRef, const std::shared_ptr<Socket>& listenSocket, [[maybe_unused]] HrtNetworkMode netMode)
     149              : {
     150            3 :     std::string ipAddr(listenSocket->GetLocalIp().GetIpStr());
     151            3 :     u32 port = listenSocket->GetListenPort();
     152            9 :     HCCL_INFO("[PreemptPortManager::%s] releasing socket, ip[%s], port[%u].", __func__, ipAddr.c_str(), port);
     153              : 
     154            3 :     bool isListening = IsAlreadyListening(portRef, ipAddr, port);
     155              :     // 释放的端口并非正在抢占的端口
     156            3 :     CHK_PRT_RET(
     157              :         !isListening,
     158              :         HCCL_WARNING(
     159              :             "[PreemptPortManager::%s] socket ip[%s], port[%u] is not preempted or has already been released.", __func__,
     160              :             ipAddr.c_str(), port), );
     161              : 
     162              :     // 释放的端口计数异常
     163            3 :     Referenced& ref = portRef[ipAddr].second;
     164            8 :     CHK_PRT_THROW(
     165              :         ref.Count() <= 0,
     166              :         HCCL_ERROR(
     167              :             "[PreemptPortManager::%s] ref[%d], ip[%s] port[%u] has already been released.", __func__, ref.Count(),
     168              :             ipAddr.c_str(), port),
     169              :         InvalidParamsException, "socket port dulplicate release");
     170              : 
     171              :     // 释放绑定端口的Socket
     172            2 :     listenSocket->StopListen();
     173            2 :     int count = ref.Unref();
     174            5 :     CHK_PRT_RET(
     175              :         count > 0,
     176              :         HCCL_INFO(
     177              :             "[PreemptPortManager::%s] release a socket on ip[%s], port[%u], ref[%d].", __func__, ipAddr.c_str(), port,
     178              :             count), );
     179              : 
     180              :     // 如果端口的计数归零,则不再抢占该端口
     181            1 :     portRef.erase(ipAddr);
     182            3 :     HCCL_INFO(
     183              :         "[PreemptPortManager::%s] release preemption of socket on ip[%s], port[%u].", __func__, ipAddr.c_str(), port);
     184            3 : }
     185              : 
     186            2 : bool PreemptPortManager::IsAlreadyListening(const IpPortRef& ipPortRef, const std::string& ipAddr, const u32 port)
     187              : {
     188            2 :     auto iterPortRef = ipPortRef.find(ipAddr);
     189            6 :     return iterPortRef != ipPortRef.end() && iterPortRef->second.first == port
     190            6 :            && iterPortRef->second.second.Count() > 0;
     191              : }
     192              : 
     193            2 : std::string PreemptPortManager::GetRangeStr(const std::vector<SocketPortRange>& portRangeVec)
     194              : {
     195            2 :     std::ostringstream portRangeOss;
     196            4 :     for (auto range : portRangeVec) {
     197            2 :         portRangeOss << " [" << std::to_string(range.min) << ", " << std::to_string(range.max) << "]";
     198              :     }
     199            4 :     return portRangeOss.str();
     200            2 : }
     201              : } // namespace Hccl
        

Generated by: LCOV version 2.0-1