已合并
[feat] 支持通过环境变量配置多QP UDP源端口 #5135
刘宏明创建于 9月8日
[feat] 支持通过环境变量配置多QP UDP源端口 #5135
已合并
共 15 个文件变更+578-276
| @@ -39,6 +39,7 @@ | |||
| 39 | #include "channel_config.h" | 39 | #include "channel_config.h" |
| 40 | #include "hcclCommDfx.h" | 40 | #include "hcclCommDfx.h" |
| 41 | #include "coll_comm_res_c_adpt.h" | 41 | #include "coll_comm_res_c_adpt.h" |
| 42 | +#include "roce_channel_desc_configurator.h" | ||
| 42 | #include "common/loggers/channel_logger.h" | 43 | #include "common/loggers/channel_logger.h" |
| 43 | 44 | ||
| 44 | using namespace hccl; | 45 | using namespace hccl; |
| @@ -76,7 +77,7 @@ static u32 ResolveQueueNum(const Hccl::EnvRdmaConfig& rdmaConfig, const HcclChan | |||
| 76 | if (channelDesc.channelProtocol == COMM_PROTOCOL_ROCE | 77 | if (channelDesc.channelProtocol == COMM_PROTOCOL_ROCE |
| 77 | && channelDesc.localEndpoint.loc.locType == ENDPOINT_LOC_TYPE_HOST) { | 78 | && channelDesc.localEndpoint.loc.locType == ENDPOINT_LOC_TYPE_HOST) { |
| 78 | u32 hostQpCount = 0; | 79 | u32 hostQpCount = 0; |
| 79 | - MyRankUtils::ReadHostNicMultiQpCount(hostQpCount); | 80 | + RoceChannelDescConfigurator::ReadHostNicMultiQpCount(hostQpCount); |
| 80 | if (hostQpCount > 0) { | 81 | if (hostQpCount > 0) { |
| 81 | return hostQpCount; | 82 | return hostQpCount; |
| 82 | } | 83 | } |
| @@ -10,6 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | set(src_list | 11 | set(src_list |
| 12 | my_rank.cc | 12 | my_rank.cc |
| 13 | + roce_channel_desc_configurator.cc | ||
| 13 | exchange_info_mgr.cc | 14 | exchange_info_mgr.cc |
| 14 | shared_jetty_channel_pool.cc | 15 | shared_jetty_channel_pool.cc |
| 15 | ) | 16 | ) |
| @@ -25,4 +26,4 @@ endif() | |||
| 25 | 26 | ||
| 26 | add_subdirectory(comm_mems) | 27 | add_subdirectory(comm_mems) |
| 27 | add_subdirectory(comm_engine) | 28 | add_subdirectory(comm_engine) |
| 28 | -add_subdirectory(endpoints) | 29 | +add_subdirectory(endpoints) |
| @@ -31,6 +31,7 @@ | |||
| 31 | #include "ccu_res.h" | 31 | #include "ccu_res.h" |
| 32 | #include "coll_comm_mgr.h" | 32 | #include "coll_comm_mgr.h" |
| 33 | #include "new_rank_info.h" | 33 | #include "new_rank_info.h" |
| 34 | +#include "roce_channel_desc_configurator.h" | ||
| 34 | 35 | ||
| 35 | #include <acl/acl.h> | 36 | #include <acl/acl.h> |
| 36 | #include <unordered_map> | 37 | #include <unordered_map> |
| @@ -109,215 +110,8 @@ inline const char* GetTlsStatusStr(Hccl::TlsStatus tlsStatus) | |||
| 109 | return "UNKNOWN"; | 110 | return "UNKNOWN"; |
| 110 | } | 111 | } |
| 111 | 112 | ||
| 112 | -bool ParseStrictDecimal(const std::string& value, uint32_t minValue, uint32_t maxValue, uint32_t& parsed) | ||
| 113 | -{ | ||
| 114 | - const auto isDecimalDigit = [](unsigned char character) { | ||
| 115 | - return std::isdigit(character) != 0; | ||
| 116 | - }; | ||
| 117 | - const bool hasValidDecimalFormat = !value.empty() && std::all_of(value.begin(), value.end(), isDecimalDigit); | ||
| 118 | - if (!hasValidDecimalFormat) { | ||
| 119 | - HCCL_WARNING("[%s] value[%s] is not a valid decimal number", __func__, value.c_str()); | ||
| 120 | - return false; | ||
| 121 | - } | ||
| 122 | - | ||
| 123 | - errno = 0; | ||
| 124 | - char* end = nullptr; | ||
| 125 | - const unsigned long number = std::strtoul(value.c_str(), &end, 10); | ||
| 126 | - if (errno == ERANGE || end != value.c_str() + value.size()) { | ||
| 127 | - HCCL_WARNING("[%s] parse value[%s] failed", __func__, value.c_str()); | ||
| 128 | - return false; | ||
| 129 | - } | ||
| 130 | - if (number < minValue || number > maxValue) { | ||
| 131 | - HCCL_WARNING("[%s] value[%s] is out of range[%u, %u]", __func__, value.c_str(), minValue, maxValue); | ||
| 132 | - return false; | ||
| 133 | - } | ||
| 134 | - | ||
| 135 | - parsed = static_cast<uint32_t>(number); | ||
| 136 | - return true; | ||
| 137 | -} | ||
| 138 | - | ||
| 139 | -bool ParseMultiQpUdpPorts(const std::string& value, std::vector<std::uint16_t>& ports) | ||
| 140 | -{ | ||
| 141 | - if (value.empty()) { | ||
| 142 | - HCCL_WARNING("[%s] ports config is empty", __func__); | ||
| 143 | - return false; | ||
| 144 | - } | ||
| 145 | - | ||
| 146 | - std::vector<std::uint16_t> parsedPorts; | ||
| 147 | - std::size_t start = 0; | ||
| 148 | - while (start <= value.size()) { | ||
| 149 | - const std::size_t comma = value.find(',', start); | ||
| 150 | - const std::string token = value.substr(start, comma == std::string::npos ? std::string::npos : comma - start); | ||
| 151 | - uint32_t port = 0; | ||
| 152 | - if (!ParseStrictDecimal(token, 1, Hccl::MultiQpSrcPortConfig::CONFIG_SRC_PORT_ID_MAX, port)) { | ||
| 153 | - HCCL_WARNING("[%s] invalid port[%s]", __func__, token.c_str()); | ||
| 154 | - return false; | ||
| 155 | - } | ||
| 156 | - parsedPorts.emplace_back(static_cast<std::uint16_t>(port)); | ||
| 157 | - if (parsedPorts.size() > Hccl::MultiQpSrcPortConfig::CONFIG_SRC_PORT_NUM_MAX) { | ||
| 158 | - HCCL_WARNING( | ||
| 159 | - "[%s] ports count[%zu] exceeds the maximum[%u]", __func__, parsedPorts.size(), | ||
| 160 | - Hccl::MultiQpSrcPortConfig::CONFIG_SRC_PORT_NUM_MAX); | ||
| 161 | - return false; | ||
| 162 | - } | ||
| 163 | - if (comma == std::string::npos) { | ||
| 164 | - break; | ||
| 165 | - } | ||
| 166 | - start = comma + 1; | ||
| 167 | - } | ||
| 168 | - | ||
| 169 | - ports = std::move(parsedPorts); | ||
| 170 | - return true; | ||
| 171 | -} | ||
| 172 | - | ||
| 173 | -bool ReadHostConfigValue(RaInfo& info, HccnCfgKey key, std::string& value) | ||
| 174 | -{ | ||
| 175 | - std::vector<char> buffer(HOST_NIC_CONFIG_BUFFER_SIZE, '\0'); | ||
| 176 | - uint32_t valueLen = static_cast<uint32_t>(buffer.size()); | ||
| 177 | - const int ret = RaGetHccnCfg(&info, key, buffer.data(), &valueLen); | ||
| 178 | - // 配置文件不存在或配置项为空时,RaGetHccnCfg返回成功和空值,不会进入该分支 | ||
| 179 | - if (ret != 0) { | ||
| 180 | - HCCL_WARNING("[%s] RaGetHccnCfg failed, phyId[%u], key[%d], ret[%d]", __func__, info.phyId, key, ret); | ||
| 181 | - return false; | ||
| 182 | - } | ||
| 183 | - | ||
| 184 | - valueLen = std::min<uint32_t>(valueLen, static_cast<uint32_t>(buffer.size())); | ||
| 185 | - value.assign(buffer.data(), valueLen); | ||
| 186 | - if (!value.empty() && value.back() == '\0') { | ||
| 187 | - value.pop_back(); | ||
| 188 | - } | ||
| 189 | - return true; | ||
| 190 | -} | ||
| 191 | - | ||
| 192 | -static bool | ||
| 193 | -ReadHostNicMultiQpConfigItem(uint32_t devicePhyId, HccnCfgKey key, const char* itemName, std::string& itemValue) | ||
| 194 | -{ | ||
| 195 | - RaInfo info{}; | ||
| 196 | - info.mode = NETWORK_PEER_ONLINE; | ||
| 197 | - info.phyId = devicePhyId; | ||
| 198 | - std::string mode; | ||
| 199 | - if (!ReadHostConfigValue(info, HCCN_CFG_UDP_PORT_MODE, mode)) { | ||
| 200 | - return false; | ||
| 201 | - } | ||
| 202 | - if (mode.empty()) { | ||
| 203 | - HCCL_WARNING("[%s] no host multi-qp config, phyId[%u]", __func__, devicePhyId); | ||
| 204 | - return false; | ||
| 205 | - } | ||
| 206 | - if (mode != "multi_qp") { | ||
| 207 | - HCCL_WARNING("[%s] invalid udp_port_mode[%s], phyId[%u]", __func__, mode.c_str(), devicePhyId); | ||
| 208 | - return false; | ||
| 209 | - } | ||
| 210 | - | ||
| 211 | - if (!ReadHostConfigValue(info, key, itemValue)) { | ||
| 212 | - return false; | ||
| 213 | - } | ||
| 214 | - if (itemValue.empty()) { | ||
| 215 | - HCCL_WARNING("[%s] incomplete host multi-qp config, phyId[%u] %s[0]", __func__, devicePhyId, itemName); | ||
| 216 | - return false; | ||
| 217 | - } | ||
| 218 | - return true; | ||
| 219 | -} | ||
| 220 | - | ||
| 221 | -void ReadHostNicMultiQpCount(uint32_t& qpCount) | ||
| 222 | -{ | ||
| 223 | - qpCount = 0; | ||
| 224 | - s32 deviceLogicId = INVALID_INT; | ||
| 225 | - uint32_t devicePhyId = INVALID_UINT; | ||
| 226 | - if ((hrtGetDevice(&deviceLogicId) != HCCL_SUCCESS) | ||
| 227 | - || (hrtGetDevicePhyIdByIndex(static_cast<uint32_t>(deviceLogicId), devicePhyId, false) != HCCL_SUCCESS)) { | ||
| 228 | - return; | ||
| 229 | - } | ||
| 230 | - | ||
| 231 | - std::string count; | ||
| 232 | - if (!ReadHostNicMultiQpConfigItem(devicePhyId, HCCN_CFG_MULTI_QP_COUNT, "multi_qp_count", count)) { | ||
| 233 | - return; | ||
| 234 | - } | ||
| 235 | - | ||
| 236 | - uint32_t parsedQpCount = 0; | ||
| 237 | - if (!ParseStrictDecimal(count, 1, Hccl::MultiQpSrcPortConfig::CONFIG_SRC_PORT_NUM_MAX, parsedQpCount)) { | ||
| 238 | - HCCL_WARNING("[%s] invalid multi_qp_count[%s], phyId[%u]", __func__, count.c_str(), devicePhyId); | ||
| 239 | - return; | ||
| 240 | - } | ||
| 241 | - | ||
| 242 | - qpCount = parsedQpCount; | ||
| 243 | - HCCL_INFO("[%s] host config valid, phyId[%u] count[%u]", __func__, devicePhyId, qpCount); | ||
| 244 | -} | ||
| 245 | - | ||
| 246 | -void ReadHostNicMultiQpUdpPorts(std::vector<std::uint16_t>& qpUdpPorts) | ||
| 247 | -{ | ||
| 248 | - qpUdpPorts.clear(); | ||
| 249 | - s32 deviceLogicId = INVALID_INT; | ||
| 250 | - uint32_t devicePhyId = INVALID_UINT; | ||
| 251 | - if ((hrtGetDevice(&deviceLogicId) != HCCL_SUCCESS) | ||
| 252 | - || (hrtGetDevicePhyIdByIndex(static_cast<uint32_t>(deviceLogicId), devicePhyId, false) != HCCL_SUCCESS)) { | ||
| 253 | - return; | ||
| 254 | - } | ||
| 255 | - | ||
| 256 | - std::string ports; | ||
| 257 | - if (!ReadHostNicMultiQpConfigItem(devicePhyId, HCCN_CFG_MULTI_QP_UDP_PORTS, "multi_qp_udp_ports", ports)) { | ||
| 258 | - return; | ||
| 259 | - } | ||
| 260 | - | ||
| 261 | - std::vector<std::uint16_t> parsedQpUdpPorts; | ||
| 262 | - if (!ParseMultiQpUdpPorts(ports, parsedQpUdpPorts)) { | ||
| 263 | - HCCL_WARNING("[%s] invalid multi_qp_udp_ports[%s], phyId[%u]", __func__, ports.c_str(), devicePhyId); | ||
| 264 | - return; | ||
| 265 | - } | ||
| 266 | - | ||
| 267 | - qpUdpPorts = std::move(parsedQpUdpPorts); | ||
| 268 | - HCCL_INFO("[%s] host config valid, phyId[%u] ports[%zu]", __func__, devicePhyId, qpUdpPorts.size()); | ||
| 269 | -} | ||
| 270 | - | ||
| 271 | } // namespace MyRankUtils | 113 | } // namespace MyRankUtils |
| 272 | 114 | ||
| 273 | -HcclResult MyRankUtils::FillRoceSrcPortList( | ||
| 274 | - const HcclChannelDesc& hcclDesc, HcommChannelDesc& hcommDesc, std::vector<uint16_t>& srcPortBuf) | ||
| 275 | -{ | ||
| 276 | - hcommDesc.roceAttr.srcPortList = nullptr; | ||
| 277 | - if (hcommDesc.remoteEndpoint.protocol != COMM_PROTOCOL_ROCE || hcommDesc.exchangeAllMems) { | ||
| 278 | - HCCL_INFO( | ||
| 279 | - "[%s] skip: protocol[%d] exchangeAllMems[%d]", __func__, hcommDesc.remoteEndpoint.protocol, | ||
| 280 | - hcommDesc.exchangeAllMems); | ||
| 281 | - return HCCL_SUCCESS; | ||
| 282 | - } | ||
| 283 | - | ||
| 284 | - std::vector<std::uint16_t> ports; | ||
| 285 | - ReadHostNicMultiQpUdpPorts(ports); | ||
| 286 | - if (ports.empty()) { | ||
| 287 | - const auto& qpSrcPortConfig = Hccl::EnvConfig::GetInstance().GetRdmaConfig().GetMultiQpSrcPortConfig(); | ||
| 288 | - if (!qpSrcPortConfig.IsAvailable()) { | ||
| 289 | - HCCL_INFO( | ||
| 290 | - "[%s] skip: multiQpSrcPortConfig not available (env HCCL_RDMA_QP_PORT_CONFIG_PATH unset or " | ||
| 291 | - "MultiQpSrcPort.cfg empty)", | ||
| 292 | - __func__); | ||
| 293 | - return HCCL_SUCCESS; | ||
| 294 | - } | ||
| 295 | - Hccl::IpAddress localIp; | ||
| 296 | - Hccl::IpAddress remoteIp; | ||
| 297 | - HcclResult localRet = CommAddrToIpAddress(hcclDesc.localEndpoint.commAddr, localIp); | ||
| 298 | - HcclResult remoteRet = CommAddrToIpAddress(hcclDesc.remoteEndpoint.commAddr, remoteIp); | ||
| 299 | - CHK_PRT_RET( | ||
| 300 | - localRet != HCCL_SUCCESS || remoteRet != HCCL_SUCCESS, | ||
| 301 | - HCCL_ERROR("[%s] CommAddrToIpAddress failed: localRet[%d] remoteRet[%d]", __func__, localRet, remoteRet), | ||
| 302 | - HCCL_E_INTERNAL); | ||
| 303 | - ports = Hccl::GetMultiQpSrcPortsByIpPair(qpSrcPortConfig, localIp, remoteIp); | ||
| 304 | - if (ports.empty()) { | ||
| 305 | - HCCL_INFO( | ||
| 306 | - "[%s] skip: no matching ports for localIp[%s] remoteIp[%s]", __func__, localIp.GetIpStr().c_str(), | ||
| 307 | - remoteIp.GetIpStr().c_str()); | ||
| 308 | - return HCCL_SUCCESS; | ||
| 309 | - } | ||
| 310 | - } | ||
| 311 | - u32 queueNum = hcommDesc.roceAttr.queueNum; | ||
| 312 | - srcPortBuf.resize(queueNum); | ||
| 313 | - for (u32 j = 0; j < queueNum; ++j) { | ||
| 314 | - srcPortBuf[j] = ports[j % ports.size()]; | ||
| 315 | - } | ||
| 316 | - hcommDesc.roceAttr.srcPortList = srcPortBuf.data(); | ||
| 317 | - HCCL_INFO("[%s] success: queueNum[%u] portCount[%zu]", __func__, queueNum, ports.size()); | ||
| 318 | - return HCCL_SUCCESS; | ||
| 319 | -} | ||
| 320 | - | ||
| 321 | namespace hccl { | 115 | namespace hccl { |
| 322 | 116 | ||
| 323 | constexpr uint32_t UNREUSE_CHANNEL_IDX = 0xFFFFFFFF; | 117 | constexpr uint32_t UNREUSE_CHANNEL_IDX = 0xFFFFFFFF; |
| @@ -1452,15 +1246,12 @@ HcclResult MyRank::CreateChannels( | |||
| 1452 | auto& rdmaConfig = Hccl::EnvConfig::GetInstance().GetRdmaConfig(); | 1246 | auto& rdmaConfig = Hccl::EnvConfig::GetInstance().GetRdmaConfig(); |
| 1453 | std::vector<HcommChannelDesc> hcommDescs(channelNum); | 1247 | std::vector<HcommChannelDesc> hcommDescs(channelNum); |
| 1454 | std::vector<std::vector<MemHandle>> allHandles(channelNum); | 1248 | std::vector<std::vector<MemHandle>> allHandles(channelNum); |
| 1455 | - // srcPortBuffers 作为 hcommDescs[i].roceAttr.srcPortList 的底层 buffer,由 FillRoceSrcPortList 填充; | 1249 | + RoceChannelDescConfigurator roceDescConfigurator(channelNum); |
| 1456 | - // 生命周期需覆盖到通道构造完成(HostCpuRoceChannel 构造时深拷贝到自身 srcPortBuf_)。 | ||
| 1457 | - std::vector<std::vector<uint16_t>> srcPortBuffers(channelNum); | ||
| 1458 | for (u32 i = 0; i < channelNum; ++i) { | 1250 | for (u32 i = 0; i < channelNum; ++i) { |
| 1459 | hcommDescs[i] = MyRankUtils::ChannelDescHccl2Hcomm(channelDescs[i], config_); | 1251 | hcommDescs[i] = MyRankUtils::ChannelDescHccl2Hcomm(channelDescs[i], config_); |
| 1460 | hcommDescs[i].roceAttr.qpThreshold = rdmaConfig.GetRdmaMultiQpThreshold(); | 1252 | hcommDescs[i].roceAttr.qpThreshold = rdmaConfig.GetRdmaMultiQpThreshold(); |
| 1461 | CHK_RET(ConfigSqDepthByExpansionMode(engine, hcommDescs[i])); | 1253 | CHK_RET(ConfigSqDepthByExpansionMode(engine, hcommDescs[i])); |
| 1462 | - | 1254 | + CHK_RET(roceDescConfigurator.FillRoceSrcPortList(channelDescs[i], i, hcommDescs[i])); |
| 1463 | - CHK_RET(MyRankUtils::FillRoceSrcPortList(channelDescs[i], hcommDescs[i], srcPortBuffers[i])); | ||
| 1464 | } | 1255 | } |
| 1465 | 1256 | ||
| 1466 | auto start = std::chrono::steady_clock::now(); | 1257 | auto start = std::chrono::steady_clock::now(); |
| @@ -0,0 +1,261 @@ | |||
| 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 "roce_channel_desc_configurator.h" | ||
| 12 | + | ||
| 13 | +#include "adapter_rts_common.h" | ||
| 14 | +#include "env_config/env_config_v2.h" | ||
| 15 | +#include "hcomm_adapter_hccp.h" | ||
| 16 | +#include "orion_adpt_utils.h" | ||
| 17 | + | ||
| 18 | +static bool ReadHostConfigValue(RaInfo& info, HccnCfgKey key, std::string& value) | ||
| 19 | +{ | ||
| 20 | + std::vector<char> buffer(hccl::RoceChannelDescConfigurator::HOST_NIC_CONFIG_BUFFER_SIZE, '\0'); | ||
| 21 | + uint32_t valueLen = static_cast<uint32_t>(buffer.size()); | ||
| 22 | + const int ret = RaGetHccnCfg(&info, key, buffer.data(), &valueLen); | ||
| 23 | + // 配置文件不存在或配置项为空时,RaGetHccnCfg返回成功和空值,不会进入该分支 | ||
| 24 | + if (ret != 0) { | ||
| 25 | + HCCL_WARNING("[%s] RaGetHccnCfg failed, phyId[%u], key[%d], ret[%d]", __func__, info.phyId, key, ret); | ||
| 26 | + return false; | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + valueLen = std::min<uint32_t>(valueLen, static_cast<uint32_t>(buffer.size())); | ||
| 30 | + value.assign(buffer.data(), valueLen); | ||
| 31 | + if (!value.empty() && value.back() == '\0') { | ||
| 32 | + value.pop_back(); | ||
| 33 | + } | ||
| 34 | + return true; | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +static bool | ||
| 38 | +ReadHostNicMultiQpConfigItem(uint32_t devicePhyId, HccnCfgKey key, const char* itemName, std::string& itemValue) | ||
| 39 | +{ | ||
| 40 | + RaInfo info{}; | ||
| 41 | + info.mode = NETWORK_PEER_ONLINE; | ||
| 42 | + info.phyId = devicePhyId; | ||
| 43 | + std::string mode; | ||
| 44 | + if (!ReadHostConfigValue(info, HCCN_CFG_UDP_PORT_MODE, mode)) { | ||
| 45 | + return false; | ||
| 46 | + } | ||
| 47 | + if (mode.empty()) { | ||
| 48 | + HCCL_WARNING("[%s] no host multi-qp config, phyId[%u]", __func__, devicePhyId); | ||
| 49 | + return false; | ||
| 50 | + } | ||
| 51 | + if (mode != "multi_qp") { | ||
| 52 | + HCCL_WARNING("[%s] invalid udp_port_mode[%s], phyId[%u]", __func__, mode.c_str(), devicePhyId); | ||
| 53 | + return false; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + if (!ReadHostConfigValue(info, key, itemValue)) { | ||
| 57 | + return false; | ||
| 58 | + } | ||
| 59 | + if (itemValue.empty()) { | ||
| 60 | + HCCL_WARNING("[%s] incomplete host multi-qp config, phyId[%u] %s[0]", __func__, devicePhyId, itemName); | ||
| 61 | + return false; | ||
| 62 | + } | ||
| 63 | + return true; | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +namespace hccl { | ||
| 67 | + | ||
| 68 | +constexpr std::size_t RoceChannelDescConfigurator::HOST_NIC_CONFIG_BUFFER_SIZE; | ||
| 69 | + | ||
| 70 | +RoceChannelDescConfigurator::RoceChannelDescConfigurator(uint32_t channelNum) : srcPortBuffers_(channelNum) {} | ||
| 71 | + | ||
| 72 | +bool RoceChannelDescConfigurator::ParseStrictDecimal( | ||
| 73 | + const std::string& value, uint32_t minValue, uint32_t maxValue, uint32_t& parsed) | ||
| 74 | +{ | ||
| 75 | + const auto isDecimalDigit = [](unsigned char character) { | ||
| 76 | + return std::isdigit(character) != 0; | ||
| 77 | + }; | ||
| 78 | + const bool hasValidDecimalFormat = !value.empty() && std::all_of(value.begin(), value.end(), isDecimalDigit); | ||
| 79 | + if (!hasValidDecimalFormat) { | ||
| 80 | + HCCL_WARNING("[%s] value[%s] is not a valid decimal number", __func__, value.c_str()); | ||
| 81 | + return false; | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + errno = 0; | ||
| 85 | + char* end = nullptr; | ||
| 86 | + const unsigned long number = std::strtoul(value.c_str(), &end, 10); | ||
| 87 | + if (errno == ERANGE || end != value.c_str() + value.size()) { | ||
| 88 | + HCCL_WARNING("[%s] parse value[%s] failed", __func__, value.c_str()); | ||
| 89 | + return false; | ||
| 90 | + } | ||
| 91 | + if (number < minValue || number > maxValue) { | ||
| 92 | + HCCL_WARNING("[%s] value[%s] is out of range[%u, %u]", __func__, value.c_str(), minValue, maxValue); | ||
| 93 | + return false; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + parsed = static_cast<uint32_t>(number); | ||
| 97 | + return true; | ||
| 98 | +} | ||
| 99 | + | ||
| 100 | +void RoceChannelDescConfigurator::ReadHostNicMultiQpCount(uint32_t& qpCount) | ||
| 101 | +{ | ||
| 102 | + qpCount = 0; | ||
| 103 | + s32 deviceLogicId = INVALID_INT; | ||
| 104 | + uint32_t devicePhyId = INVALID_UINT; | ||
| 105 | + if ((hrtGetDevice(&deviceLogicId) != HCCL_SUCCESS) | ||
| 106 | + || (hrtGetDevicePhyIdByIndex(static_cast<uint32_t>(deviceLogicId), devicePhyId, false) != HCCL_SUCCESS)) { | ||
| 107 | + return; | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + std::string count; | ||
| 111 | + if (!ReadHostNicMultiQpConfigItem(devicePhyId, HCCN_CFG_MULTI_QP_COUNT, "multi_qp_count", count)) { | ||
| 112 | + return; | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + uint32_t parsedQpCount = 0; | ||
| 116 | + if (!ParseStrictDecimal(count, 1, Hccl::MultiQpSrcPortConfig::CONFIG_SRC_PORT_NUM_MAX, parsedQpCount)) { | ||
| 117 | + HCCL_WARNING("[%s] invalid multi_qp_count[%s], phyId[%u]", __func__, count.c_str(), devicePhyId); | ||
| 118 | + return; | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + qpCount = parsedQpCount; | ||
| 122 | + HCCL_INFO("[%s] host config valid, phyId[%u] count[%u]", __func__, devicePhyId, qpCount); | ||
| 123 | +} | ||
| 124 | + | ||
| 125 | +void RoceChannelDescConfigurator::ReadHostNicMultiQpUdpPorts(std::vector<uint16_t>& qpUdpPorts) | ||
| 126 | +{ | ||
| 127 | + qpUdpPorts.clear(); | ||
| 128 | + s32 deviceLogicId = INVALID_INT; | ||
| 129 | + uint32_t devicePhyId = INVALID_UINT; | ||
| 130 | + if ((hrtGetDevice(&deviceLogicId) != HCCL_SUCCESS) | ||
| 131 | + || (hrtGetDevicePhyIdByIndex(static_cast<uint32_t>(deviceLogicId), devicePhyId, false) != HCCL_SUCCESS)) { | ||
| 132 | + return; | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | + std::string ports; | ||
| 136 | + if (!ReadHostNicMultiQpConfigItem(devicePhyId, HCCN_CFG_MULTI_QP_UDP_PORTS, "multi_qp_udp_ports", ports)) { | ||
| 137 | + return; | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + std::vector<uint16_t> parsedQpUdpPorts; | ||
| 141 | + if (!Hccl::ParseHostRdmaUdpPorts(ports, parsedQpUdpPorts)) { | ||
| 142 | + HCCL_WARNING("[%s] invalid multi_qp_udp_ports[%s], phyId[%u]", __func__, ports.c_str(), devicePhyId); | ||
| 143 | + return; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + qpUdpPorts = std::move(parsedQpUdpPorts); | ||
| 147 | + HCCL_INFO("[%s] host config valid, phyId[%u] ports[%zu]", __func__, devicePhyId, qpUdpPorts.size()); | ||
| 148 | +} | ||
| 149 | + | ||
| 150 | +void RoceChannelDescConfigurator::FillPortsFromHostRdmaUdpPortsList(std::vector<uint16_t>& ports) | ||
| 151 | +{ | ||
| 152 | + const auto& udpPortsList = Hccl::EnvConfig::GetInstance().GetRdmaConfig().GetHostRdmaUdpPortsList(); | ||
| 153 | + if (!udpPortsList.IsAvailable()) { | ||
| 154 | + HCCL_INFO( | ||
| 155 | + "[%s] hostRdmaUdpPortsList not available (env HCCL_HOST_RDMA_UDP_PORTS_LIST unset or empty)", __func__); | ||
| 156 | + return; | ||
| 157 | + } | ||
| 158 | + | ||
| 159 | + s32 deviceLogicId = INVALID_INT; | ||
| 160 | + uint32_t devicePhyId = INVALID_UINT; | ||
| 161 | + if ((hrtGetDevice(&deviceLogicId) != HCCL_SUCCESS) | ||
| 162 | + || (hrtGetDevicePhyIdByIndex(static_cast<uint32_t>(deviceLogicId), devicePhyId, false) != HCCL_SUCCESS)) { | ||
| 163 | + HCCL_WARNING("[%s] hrtGetDevice or hrtGetDevicePhyIdByIndex failed, fall back to MultiQpSrcPort.cfg", __func__); | ||
| 164 | + return; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + const auto portIter = udpPortsList.portsByPhyId.find(devicePhyId); | ||
| 168 | + if (portIter != udpPortsList.portsByPhyId.end()) { | ||
| 169 | + ports = portIter->second; | ||
| 170 | + } | ||
| 171 | + if (ports.empty()) { | ||
| 172 | + HCCL_INFO("[%s] no matching HCCL_HOST_RDMA_UDP_PORTS_LIST ports for phyId[%u]", __func__, devicePhyId); | ||
| 173 | + } | ||
| 174 | +} | ||
| 175 | + | ||
| 176 | +HcclResult RoceChannelDescConfigurator::FillPortsFromMultiQpSrcPortConfig( | ||
| 177 | + const HcclChannelDesc& hcclDesc, std::vector<uint16_t>& ports) | ||
| 178 | +{ | ||
| 179 | + const auto& qpSrcPortConfig = Hccl::EnvConfig::GetInstance().GetRdmaConfig().GetMultiQpSrcPortConfig(); | ||
| 180 | + if (!qpSrcPortConfig.IsAvailable()) { | ||
| 181 | + HCCL_INFO( | ||
| 182 | + "[%s] skip: multiQpSrcPortConfig not available (env HCCL_RDMA_QP_PORT_CONFIG_PATH unset or " | ||
| 183 | + "MultiQpSrcPort.cfg empty)", | ||
| 184 | + __func__); | ||
| 185 | + return HCCL_SUCCESS; | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + Hccl::IpAddress localIp; | ||
| 189 | + Hccl::IpAddress remoteIp; | ||
| 190 | + HcclResult localRet = hcomm::CommAddrToIpAddress(hcclDesc.localEndpoint.commAddr, localIp); | ||
| 191 | + HcclResult remoteRet = hcomm::CommAddrToIpAddress(hcclDesc.remoteEndpoint.commAddr, remoteIp); | ||
| 192 | + CHK_PRT_RET( | ||
| 193 | + localRet != HCCL_SUCCESS || remoteRet != HCCL_SUCCESS, | ||
| 194 | + HCCL_ERROR("[%s] CommAddrToIpAddress failed: localRet[%d] remoteRet[%d]", __func__, localRet, remoteRet), | ||
| 195 | + HCCL_E_INTERNAL); | ||
| 196 | + ports = Hccl::GetMultiQpSrcPortsByIpPair(qpSrcPortConfig, localIp, remoteIp); | ||
| 197 | + if (ports.empty()) { | ||
| 198 | + HCCL_INFO( | ||
| 199 | + "[%s] skip: no matching ports for localIp[%s] remoteIp[%s]", __func__, localIp.GetIpStr().c_str(), | ||
| 200 | + remoteIp.GetIpStr().c_str()); | ||
| 201 | + } | ||
| 202 | + return HCCL_SUCCESS; | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +HcclResult | ||
| 206 | +RoceChannelDescConfigurator::ResolveRoceSrcPorts(const HcclChannelDesc& hcclDesc, std::vector<uint16_t>& ports) | ||
| 207 | +{ | ||
| 208 | + ports.clear(); | ||
| 209 | + ReadHostNicMultiQpUdpPorts(ports); | ||
| 210 | + if (!ports.empty()) { | ||
| 211 | + return HCCL_SUCCESS; | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + FillPortsFromHostRdmaUdpPortsList(ports); | ||
| 215 | + if (!ports.empty()) { | ||
| 216 | + return HCCL_SUCCESS; | ||
| 217 | + } | ||
| 218 | + | ||
| 219 | + return FillPortsFromMultiQpSrcPortConfig(hcclDesc, ports); | ||
| 220 | +} | ||
| 221 | + | ||
| 222 | +HcclResult RoceChannelDescConfigurator::FillRoceSrcPortList( | ||
| 223 | + const HcclChannelDesc& hcclDesc, uint32_t channelIndex, HcommChannelDesc& hcommDesc) | ||
| 224 | +{ | ||
| 225 | + hcommDesc.roceAttr.srcPortList = nullptr; | ||
| 226 | + if (hcclDesc.localEndpoint.loc.locType != ENDPOINT_LOC_TYPE_HOST | ||
| 227 | + || hcommDesc.remoteEndpoint.protocol != COMM_PROTOCOL_ROCE || hcommDesc.exchangeAllMems) { | ||
| 228 | + HCCL_INFO( | ||
| 229 | + "[%s] skip: localLocType[%d] protocol[%d] exchangeAllMems[%d]", __func__, | ||
| 230 | + hcclDesc.localEndpoint.loc.locType, hcommDesc.remoteEndpoint.protocol, hcommDesc.exchangeAllMems); | ||
| 231 | + return HCCL_SUCCESS; | ||
| 232 | + } | ||
| 233 | + CHK_PRT_RET( | ||
| 234 | + channelIndex >= srcPortBuffers_.size(), | ||
| 235 | + HCCL_ERROR( | ||
| 236 | + "[%s] channelIndex[%u] is out of range, channelNum[%zu]", __func__, channelIndex, srcPortBuffers_.size()), | ||
| 237 | + HCCL_E_PARA); | ||
| 238 | + | ||
| 239 | + std::vector<uint16_t> ports; | ||
| 240 | + CHK_RET(ResolveRoceSrcPorts(hcclDesc, ports)); | ||
| 241 | + if (ports.empty()) { | ||
| 242 | + return HCCL_SUCCESS; | ||
| 243 | + } | ||
| 244 | + const uint32_t queueNum = hcommDesc.roceAttr.queueNum; | ||
| 245 | + if (ports.size() != static_cast<std::size_t>(queueNum)) { | ||
| 246 | + HCCL_RUN_WARNING( | ||
| 247 | + "[%s] UDP source port count[%zu] does not match queueNum[%u], ports will be truncated or reused " | ||
| 248 | + "cyclically", | ||
| 249 | + __func__, ports.size(), queueNum); | ||
| 250 | + } | ||
| 251 | + auto& srcPortBuffer = srcPortBuffers_[channelIndex]; | ||
| 252 | + srcPortBuffer.resize(queueNum); | ||
| 253 | + for (uint32_t index = 0; index < queueNum; ++index) { | ||
| 254 | + srcPortBuffer[index] = ports[index % ports.size()]; | ||
| 255 | + } | ||
| 256 | + hcommDesc.roceAttr.srcPortList = srcPortBuffer.data(); | ||
| 257 | + HCCL_INFO("[%s] success: queueNum[%u] portCount[%zu]", __func__, queueNum, ports.size()); | ||
| 258 | + return HCCL_SUCCESS; | ||
| 259 | +} | ||
| 260 | + | ||
| 261 | +} // namespace hccl | ||
| @@ -0,0 +1,55 @@ | |||
| 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 | +#ifndef ROCE_CHANNEL_DESC_CONFIGURATOR_H | ||
| 12 | +#define ROCE_CHANNEL_DESC_CONFIGURATOR_H | ||
| 13 | + | ||
| 14 | +#include <string> | ||
| 15 | +#include <vector> | ||
| 16 | + | ||
| 17 | +#include "hccl/hccl_channel.h" | ||
| 18 | + | ||
| 19 | +namespace hccl { | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * @brief Prepares RoCE-specific parameters in HcommChannelDesc. | ||
| 23 | + * | ||
| 24 | + * The configurator owns the buffers referenced by HcommChannelDesc::roceAttr.srcPortList. Its lifetime must cover | ||
| 25 | + * channel creation so that the descriptors always reference valid storage. | ||
| 26 | + */ | ||
| 27 | +class RoceChannelDescConfigurator { | ||
| 28 | +public: | ||
| 29 | + static constexpr std::size_t HOST_NIC_CONFIG_BUFFER_SIZE = 2048; | ||
| 30 | + | ||
| 31 | + explicit RoceChannelDescConfigurator(uint32_t channelNum); | ||
| 32 | + RoceChannelDescConfigurator(const RoceChannelDescConfigurator&) = delete; | ||
| 33 | + RoceChannelDescConfigurator& operator=(const RoceChannelDescConfigurator&) = delete; | ||
| 34 | + RoceChannelDescConfigurator(RoceChannelDescConfigurator&&) = delete; | ||
| 35 | + RoceChannelDescConfigurator& operator=(RoceChannelDescConfigurator&&) = delete; | ||
| 36 | + | ||
| 37 | + // 配置 RoCE 源端口,并保证 srcPortList 指向的内存在通道创建期间有效。 | ||
| 38 | + HcclResult FillRoceSrcPortList(const HcclChannelDesc& hcclDesc, uint32_t channelIndex, HcommChannelDesc& hcommDesc); | ||
| 39 | + | ||
| 40 | + // 读取并校验 Host 网卡的多 QP 数量。 | ||
| 41 | + static void ReadHostNicMultiQpCount(uint32_t& qpCount); | ||
| 42 | + | ||
| 43 | +private: | ||
| 44 | + static bool ParseStrictDecimal(const std::string& value, uint32_t minValue, uint32_t maxValue, uint32_t& parsed); | ||
| 45 | + static void ReadHostNicMultiQpUdpPorts(std::vector<uint16_t>& qpUdpPorts); | ||
| 46 | + static void FillPortsFromHostRdmaUdpPortsList(std::vector<uint16_t>& ports); | ||
| 47 | + static HcclResult FillPortsFromMultiQpSrcPortConfig(const HcclChannelDesc& hcclDesc, std::vector<uint16_t>& ports); | ||
| 48 | + static HcclResult ResolveRoceSrcPorts(const HcclChannelDesc& hcclDesc, std::vector<uint16_t>& ports); | ||
| 49 | + | ||
| 50 | + std::vector<std::vector<uint16_t>> srcPortBuffers_; | ||
| 51 | +}; | ||
| 52 | + | ||
| 53 | +} // namespace hccl | ||
| 54 | + | ||
| 55 | +#endif // ROCE_CHANNEL_DESC_CONFIGURATOR_H | ||
| @@ -133,6 +133,11 @@ void EnvRdmaConfig::Parse() | |||
| 133 | "[HCCL_ENV] HCCL_MULTI_QP_THRESHOLD set by %s to [%u]B", multiQpThreshold.GetSource(), | 133 | "[HCCL_ENV] HCCL_MULTI_QP_THRESHOLD set by %s to [%u]B", multiQpThreshold.GetSource(), |
| 134 | GetRdmaMultiQpThreshold()); | 134 | GetRdmaMultiQpThreshold()); |
| 135 | 135 | ||
| 136 | + hostRdmaUdpPortsList.Parse(); | ||
| 137 | + HCCL_RUN_INFO( | ||
| 138 | + "[HCCL_ENV] HCCL_HOST_RDMA_UDP_PORTS_LIST set by %s, device config count[%zu]", | ||
| 139 | + hostRdmaUdpPortsList.GetSource(), GetHostRdmaUdpPortsList().GetDeviceConfigCount()); | ||
| 140 | + | ||
| 136 | qpPortConfigPath.Parse(); | 141 | qpPortConfigPath.Parse(); |
| 137 | HCCL_RUN_INFO( | 142 | HCCL_RUN_INFO( |
| 138 | "[HCCL_ENV] HCCL_RDMA_QP_PORT_CONFIG_PATH set by %s to [%s]", qpPortConfigPath.GetSource(), | 143 | "[HCCL_ENV] HCCL_RDMA_QP_PORT_CONFIG_PATH set by %s to [%s]", qpPortConfigPath.GetSource(), |
| @@ -363,6 +368,8 @@ u32 EnvRdmaConfig::GetRdmaQueueNum() const { return queueNum.Get(); } | |||
| 363 | 368 | ||
| 364 | u32 EnvRdmaConfig::GetRdmaMultiQpThreshold() const { return multiQpThreshold.Get(); } | 369 | u32 EnvRdmaConfig::GetRdmaMultiQpThreshold() const { return multiQpThreshold.Get(); } |
| 365 | 370 | ||
| 371 | +const HostRdmaUdpPortsList& EnvRdmaConfig::GetHostRdmaUdpPortsList() const { return hostRdmaUdpPortsList.Get(); } | ||
| 372 | + | ||
| 366 | const MultiQpSrcPortConfig& EnvRdmaConfig::GetMultiQpSrcPortConfig() const { return multiQpSrcPortConfig_; } | 373 | const MultiQpSrcPortConfig& EnvRdmaConfig::GetMultiQpSrcPortConfig() const { return multiQpSrcPortConfig_; } |
| 367 | 374 | ||
| 368 | // EnvUbConfig | 375 | // EnvUbConfig |
| @@ -126,6 +126,7 @@ public: | |||
| 126 | u32 GetRdmaRetryCnt() const; | 126 | u32 GetRdmaRetryCnt() const; |
| 127 | u32 GetRdmaQueueNum() const; | 127 | u32 GetRdmaQueueNum() const; |
| 128 | u32 GetRdmaMultiQpThreshold() const; | 128 | u32 GetRdmaMultiQpThreshold() const; |
| 129 | + const HostRdmaUdpPortsList& GetHostRdmaUdpPortsList() const; | ||
| 129 | const MultiQpSrcPortConfig& GetMultiQpSrcPortConfig() const; | 130 | const MultiQpSrcPortConfig& GetMultiQpSrcPortConfig() const; |
| 130 | 131 | ||
| 131 | static constexpr u32 HCCL_RDMA_TC_DEFAULT = 132; // 默认的traffic class为132(33*4) | 132 | static constexpr u32 HCCL_RDMA_TC_DEFAULT = 132; // 默认的traffic class为132(33*4) |
| @@ -168,6 +169,7 @@ private: | |||
| 168 | CfgField<std::string> qpPortConfigPath{ | 169 | CfgField<std::string> qpPortConfigPath{ |
| 169 | "HCCL_RDMA_QP_PORT_CONFIG_PATH", "", Str2T<std::string>, CheckFilePath, SetRealPath}; | 170 | "HCCL_RDMA_QP_PORT_CONFIG_PATH", "", Str2T<std::string>, CheckFilePath, SetRealPath}; |
| 170 | MultiQpSrcPortConfig multiQpSrcPortConfig_; | 171 | MultiQpSrcPortConfig multiQpSrcPortConfig_; |
| 172 | + CfgField<HostRdmaUdpPortsList> hostRdmaUdpPortsList{"HCCL_HOST_RDMA_UDP_PORTS_LIST", {}, CastHostRdmaUdpPortsList}; | ||
| 171 | }; | 173 | }; |
| 172 | 174 | ||
| 173 | // UB配置 | 175 | // UB配置 |
| @@ -16,8 +16,8 @@ | |||
| 16 | #include <algorithm> | 16 | #include <algorithm> |
| 17 | #include <sstream> | 17 | #include <sstream> |
| 18 | #include <array> | 18 | #include <array> |
| 19 | +#include <limits> | ||
| 19 | 20 | ||
| 20 | -#include "sal.h" | ||
| 21 | #include "string_util.h" | 21 | #include "string_util.h" |
| 22 | #include "base_config_legacy.h" | 22 | #include "base_config_legacy.h" |
| 23 | #include "orion_adapter_rts.h" | 23 | #include "orion_adapter_rts.h" |
| @@ -26,8 +26,107 @@ | |||
| 26 | 26 | ||
| 27 | namespace Hccl { | 27 | namespace Hccl { |
| 28 | 28 | ||
| 29 | +static bool ParseDecimalU32(const std::string& value, u32& parsed) | ||
| 30 | +{ | ||
| 31 | + if (value.empty() || !std::all_of(value.begin(), value.end(), [](unsigned char ch) { | ||
| 32 | + return std::isdigit(ch) != 0; | ||
| 33 | + })) { | ||
| 34 | + return false; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + errno = 0; | ||
| 38 | + char* end = nullptr; | ||
| 39 | + const unsigned long number = std::strtoul(value.c_str(), &end, 10); | ||
| 40 | + if (errno == ERANGE || end != value.c_str() + value.size() || number > std::numeric_limits<u32>::max()) { | ||
| 41 | + return false; | ||
| 42 | + } | ||
| 43 | + parsed = static_cast<u32>(number); | ||
| 44 | + return true; | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +bool ParseHostRdmaUdpPorts(const std::string& value, std::vector<std::uint16_t>& ports) | ||
| 48 | +{ | ||
| 49 | + if (value.empty() || value.back() == ',') { | ||
| 50 | + HCCL_ERROR("[ParseHostRdmaUdpPorts] UDP source port list is empty or ends with a comma."); | ||
| 51 | + return false; | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + std::vector<std::uint16_t> parsedPorts; | ||
| 55 | + size_t start = 0; | ||
| 56 | + while (true) { | ||
| 57 | + const size_t comma = value.find(',', start); | ||
| 58 | + const std::string token = value.substr(start, comma == std::string::npos ? std::string::npos : comma - start); | ||
| 59 | + u32 port = 0; | ||
| 60 | + if (!ParseDecimalU32(token, port) || port == 0 || port > MultiQpSrcPortConfig::CONFIG_SRC_PORT_ID_MAX) { | ||
| 61 | + HCCL_ERROR( | ||
| 62 | + "[ParseHostRdmaUdpPorts] UDP source port is invalid, expected a decimal integer in range [1, %u].", | ||
| 63 | + MultiQpSrcPortConfig::CONFIG_SRC_PORT_ID_MAX); | ||
| 64 | + return false; | ||
| 65 | + } | ||
| 66 | + parsedPorts.emplace_back(static_cast<std::uint16_t>(port)); | ||
| 67 | + if (parsedPorts.size() > MultiQpSrcPortConfig::CONFIG_SRC_PORT_NUM_MAX) { | ||
| 68 | + HCCL_ERROR( | ||
| 69 | + "[ParseHostRdmaUdpPorts] UDP source port count[%zu] exceeds the maximum[%u].", parsedPorts.size(), | ||
| 70 | + MultiQpSrcPortConfig::CONFIG_SRC_PORT_NUM_MAX); | ||
| 71 | + return false; | ||
| 72 | + } | ||
| 73 | + if (comma == std::string::npos) { | ||
| 74 | + break; | ||
| 75 | + } | ||
| 76 | + start = comma + 1U; | ||
| 77 | + } | ||
| 78 | + ports = std::move(parsedPorts); | ||
| 79 | + return true; | ||
| 80 | +} | ||
| 81 | + | ||
| 29 | /*----------------------------- cast functions -------------------------*/ | 82 | /*----------------------------- cast functions -------------------------*/ |
| 30 | 83 | ||
| 84 | +HostRdmaUdpPortsList CastHostRdmaUdpPortsList(const std::string& s) | ||
| 85 | +{ | ||
| 86 | + HostRdmaUdpPortsList list; | ||
| 87 | + if (s.empty()) { | ||
| 88 | + return list; | ||
| 89 | + } | ||
| 90 | + if (s.size() > HostRdmaUdpPortsList::CONFIG_VALUE_LEN_MAX) { | ||
| 91 | + HCCL_ERROR( | ||
| 92 | + "[CastHostRdmaUdpPortsList] config value length[%zu] exceeds the maximum[%u].", s.size(), | ||
| 93 | + HostRdmaUdpPortsList::CONFIG_VALUE_LEN_MAX); | ||
| 94 | + return {}; | ||
| 95 | + } | ||
| 96 | + | ||
| 97 | + size_t start = 0; | ||
| 98 | + while (start <= s.size()) { | ||
| 99 | + const size_t semicolon = s.find(';', start); | ||
| 100 | + const std::string deviceConfig | ||
| 101 | + = s.substr(start, semicolon == std::string::npos ? std::string::npos : semicolon - start); | ||
| 102 | + const size_t colon = deviceConfig.find(':'); | ||
| 103 | + if (deviceConfig.empty() || colon == std::string::npos || colon == 0U || colon + 1U >= deviceConfig.size() | ||
| 104 | + || deviceConfig.find(':', colon + 1U) != std::string::npos) { | ||
| 105 | + HCCL_ERROR("[CastHostRdmaUdpPortsList] device config has invalid separators or an empty field."); | ||
| 106 | + return {}; | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + u32 devicePhyId = 0; | ||
| 110 | + if (!ParseDecimalU32(deviceConfig.substr(0, colon), devicePhyId)) { | ||
| 111 | + HCCL_ERROR("[CastHostRdmaUdpPortsList] phy_dev_id is not a valid decimal integer."); | ||
| 112 | + return {}; | ||
| 113 | + } | ||
| 114 | + std::vector<std::uint16_t> ports; | ||
| 115 | + if (!ParseHostRdmaUdpPorts(deviceConfig.substr(colon + 1U), ports)) { | ||
| 116 | + return {}; | ||
| 117 | + } | ||
| 118 | + if (!list.portsByPhyId.emplace(devicePhyId, std::move(ports)).second) { | ||
| 119 | + HCCL_ERROR("[CastHostRdmaUdpPortsList] phy_dev_id[%u] is duplicated.", devicePhyId); | ||
| 120 | + return {}; | ||
| 121 | + } | ||
| 122 | + if (semicolon == std::string::npos) { | ||
| 123 | + break; | ||
| 124 | + } | ||
| 125 | + start = semicolon + 1U; | ||
| 126 | + } | ||
| 127 | + return list; | ||
| 128 | +} | ||
| 129 | + | ||
| 31 | bool CastBin2Bool(const std::string& s) | 130 | bool CastBin2Bool(const std::string& s) |
| 32 | { | 131 | { |
| 33 | bool b = true; | 132 | bool b = true; |
| @@ -131,9 +230,9 @@ void GetUIntFromStr(const std::string& digitStr, u32& val) | |||
| 131 | CHK_PRT_THROW( | 230 | CHK_PRT_THROW( |
| 132 | !isAllDigits, HCCL_ERROR("[GetUIntFromStr] str[%s] is not all digit.", digitStr.c_str()), | 231 | !isAllDigits, HCCL_ERROR("[GetUIntFromStr] str[%s] is not all digit.", digitStr.c_str()), |
| 133 | InvalidParamsException, "parser portRange fail."); | 232 | InvalidParamsException, "parser portRange fail."); |
| 134 | - auto ret = SalStrToULong(digitStr.c_str(), HCCL_BASE_DECIMAL, val); | 233 | + const bool parseSuccess = ParseDecimalU32(digitStr, val); |
| 135 | CHK_PRT_THROW( | 234 | CHK_PRT_THROW( |
| 136 | - ret != HCCL_SUCCESS, HCCL_ERROR("[GetUIntFromStr] str[%s] is a invalid number.", digitStr.c_str()), | 235 | + !parseSuccess, HCCL_ERROR("[GetUIntFromStr] str[%s] is a invalid number.", digitStr.c_str()), |
| 137 | InvalidParamsException, "parser portRange fail."); | 236 | InvalidParamsException, "parser portRange fail."); |
| 138 | } | 237 | } |
| 139 | 238 | ||
| @@ -963,6 +1062,15 @@ HcclDetourType CastDetourType(const std::string& s) | |||
| 963 | } | 1062 | } |
| 964 | 1063 | ||
| 965 | /*----------------------- multi qp src port config --------------------------*/ | 1064 | /*----------------------- multi qp src port config --------------------------*/ |
| 1065 | +std::vector<std::uint16_t> GetHostRdmaUdpPortsByPhyId(const HostRdmaUdpPortsList& list, u32 devicePhyId) | ||
[LOW] GetHostRdmaUdpPortsByPhyId 全仓无调用点,属本 PR 引入的死代码 问题分析:
全仓 grep 仅两处命中:本处实现与 env_func.h:274 的 extern 声明,无任何调用点(消费侧 roce_channel_desc_configurator.cc:207 直接用 建议修复: 删除 env_func.cc:1044-1051 的实现与 env_func.h:274 的 extern 声明(连同空行);若希望保留「按 phyId 查询」的语义封装,则反向处理——让 roce_channel_desc_configurator.cc:207-210 改为调用该 helper(其返回 const 引用或值拷贝均可),二选一,避免 helper 与直接成员访问并存。 ![]() ![]() | |||
| 1066 | +{ | ||
| 1067 | + const auto iter = list.portsByPhyId.find(devicePhyId); | ||
| 1068 | + if (iter == list.portsByPhyId.end()) { | ||
| 1069 | + return {}; | ||
| 1070 | + } | ||
| 1071 | + return iter->second; | ||
| 1072 | +} | ||
| 1073 | + | ||
| 966 | std::vector<std::uint16_t> | 1074 | std::vector<std::uint16_t> |
| 967 | GetMultiQpSrcPortsByIpPair(const MultiQpSrcPortConfig& config, const IpAddress& srcIp, const IpAddress& dstIp) | 1075 | GetMultiQpSrcPortsByIpPair(const MultiQpSrcPortConfig& config, const IpAddress& srcIp, const IpAddress& dstIp) |
| 968 | { | 1076 | { |
| @@ -260,6 +260,21 @@ struct MultiQpSrcPortConfig { | |||
| 260 | static constexpr u32 CONFIG_IP_NUM = 2; | 260 | static constexpr u32 CONFIG_IP_NUM = 2; |
| 261 | }; | 261 | }; |
| 262 | 262 | ||
| 263 | +struct HostRdmaUdpPortsList { | ||
| 264 | + std::unordered_map<u32, std::vector<std::uint16_t>> portsByPhyId; | ||
| 265 | + | ||
| 266 | + bool IsAvailable() const { return !portsByPhyId.empty(); } | ||
| 267 | + size_t GetDeviceConfigCount() const { return portsByPhyId.size(); } | ||
| 268 | + | ||
| 269 | + static constexpr u32 CONFIG_VALUE_LEN_MAX = 32 * 1024; | ||
| 270 | +}; | ||
| 271 | + | ||
| 272 | +extern bool ParseHostRdmaUdpPorts(const std::string& value, std::vector<std::uint16_t>& ports); | ||
| 273 | + | ||
| 274 | +extern HostRdmaUdpPortsList CastHostRdmaUdpPortsList(const std::string& s); | ||
| 275 | + | ||
| 276 | +extern std::vector<std::uint16_t> GetHostRdmaUdpPortsByPhyId(const HostRdmaUdpPortsList& list, u32 devicePhyId); | ||
| 277 | + | ||
| 263 | extern u32 | 278 | extern u32 |
| 264 | GetMultiQpPortsNumByIpPair(const MultiQpSrcPortConfig& config, const IpAddress& srcIp, const IpAddress& dstIp); | 279 | GetMultiQpPortsNumByIpPair(const MultiQpSrcPortConfig& config, const IpAddress& srcIp, const IpAddress& dstIp); |
| 265 | 280 | ||
| @@ -262,6 +262,7 @@ global: | |||
| 262 | "Hccl::EnvHostNicConfig::GetWhitelistDisable() const"; # _ZNK4Hccl16EnvHostNicConfig19GetWhitelistDisableEv | 262 | "Hccl::EnvHostNicConfig::GetWhitelistDisable() const"; # _ZNK4Hccl16EnvHostNicConfig19GetWhitelistDisableEv |
| 263 | "Hccl::EnvLogConfig::GetDfsConfig() const"; # _ZNK4Hccl12EnvLogConfig12GetDfsConfigEv | 263 | "Hccl::EnvLogConfig::GetDfsConfig() const"; # _ZNK4Hccl12EnvLogConfig12GetDfsConfigEv |
| 264 | "Hccl::EnvRdmaConfig::GetMultiQpSrcPortConfig() const"; # _ZNK4Hccl13EnvRdmaConfig23GetMultiQpSrcPortConfigEv | 264 | "Hccl::EnvRdmaConfig::GetMultiQpSrcPortConfig() const"; # _ZNK4Hccl13EnvRdmaConfig23GetMultiQpSrcPortConfigEv |
| 265 | + "Hccl::EnvRdmaConfig::GetHostRdmaUdpPortsList() const"; # _ZNK4Hccl13EnvRdmaConfig23GetHostRdmaUdpPortsListEv | ||
| 265 | "Hccl::EnvRdmaConfig::GetRdmaMultiQpThreshold() const"; # _ZNK4Hccl13EnvRdmaConfig23GetRdmaMultiQpThresholdEv | 266 | "Hccl::EnvRdmaConfig::GetRdmaMultiQpThreshold() const"; # _ZNK4Hccl13EnvRdmaConfig23GetRdmaMultiQpThresholdEv |
| 266 | "Hccl::EnvRdmaConfig::GetRdmaQueueNum() const"; # _ZNK4Hccl13EnvRdmaConfig15GetRdmaQueueNumEv | 267 | "Hccl::EnvRdmaConfig::GetRdmaQueueNum() const"; # _ZNK4Hccl13EnvRdmaConfig15GetRdmaQueueNumEv |
| 267 | "Hccl::EnvRdmaConfig::GetRdmaRetryCnt() const"; # _ZNK4Hccl13EnvRdmaConfig15GetRdmaRetryCntEv | 268 | "Hccl::EnvRdmaConfig::GetRdmaRetryCnt() const"; # _ZNK4Hccl13EnvRdmaConfig15GetRdmaRetryCntEv |
| @@ -276,6 +277,7 @@ global: | |||
| 276 | "Hccl::ExceptionInfo::GetErrorMsg(Hccl::ExceptionType const&)"; # _ZN4Hccl13ExceptionInfo11GetErrorMsgERKNS_13ExceptionTypeE | 277 | "Hccl::ExceptionInfo::GetErrorMsg(Hccl::ExceptionType const&)"; # _ZN4Hccl13ExceptionInfo11GetErrorMsgERKNS_13ExceptionTypeE |
| 277 | "Hccl::GetMultiQpPortsNumByIpPair(Hccl::MultiQpSrcPortConfig const&, Hccl::IpAddress const&, Hccl::IpAddress const&)"; # _ZN4Hccl26GetMultiQpPortsNumByIpPairERKNS_20MultiQpSrcPortConfigERKNS_9IpAddressES5_ | 278 | "Hccl::GetMultiQpPortsNumByIpPair(Hccl::MultiQpSrcPortConfig const&, Hccl::IpAddress const&, Hccl::IpAddress const&)"; # _ZN4Hccl26GetMultiQpPortsNumByIpPairERKNS_20MultiQpSrcPortConfigERKNS_9IpAddressES5_ |
| 278 | "Hccl::GetMultiQpSrcPortsByIpPair(Hccl::MultiQpSrcPortConfig const&, Hccl::IpAddress const&, Hccl::IpAddress const&)"; # _ZN4Hccl26GetMultiQpSrcPortsByIpPairERKNS_20MultiQpSrcPortConfigERKNS_9IpAddressES5_ | 279 | "Hccl::GetMultiQpSrcPortsByIpPair(Hccl::MultiQpSrcPortConfig const&, Hccl::IpAddress const&, Hccl::IpAddress const&)"; # _ZN4Hccl26GetMultiQpSrcPortsByIpPairERKNS_20MultiQpSrcPortConfigERKNS_9IpAddressES5_ |
| 280 | + "Hccl::ParseHostRdmaUdpPorts(std::string const&, std::vector<unsigned short, std::allocator<unsigned short> >&)"; # _ZN4Hccl21ParseHostRdmaUdpPortsERKSsRSt6vectorItSaItEE | ||
| 279 | "Hccl::GetUbToken()"; # _ZN4Hccl10GetUbTokenEv | 281 | "Hccl::GetUbToken()"; # _ZN4Hccl10GetUbTokenEv |
| 280 | "Hccl::GlobalMirrorTasks::FindTaskInfo(unsigned int, unsigned int, unsigned int, Hccl::TaskInfo*&) const"; # _ZNK4Hccl17GlobalMirrorTasks12FindTaskInfoEjjjRPNS_8TaskInfoE | 282 | "Hccl::GlobalMirrorTasks::FindTaskInfo(unsigned int, unsigned int, unsigned int, Hccl::TaskInfo*&) const"; # _ZNK4Hccl17GlobalMirrorTasks12FindTaskInfoEjjjRPNS_8TaskInfoE |
| 281 | "Hccl::GlobalMirrorTasks::GetQueue(unsigned int, unsigned int) const"; # _ZNK4Hccl17GlobalMirrorTasks8GetQueueEjj | 283 | "Hccl::GlobalMirrorTasks::GetQueue(unsigned int, unsigned int) const"; # _ZNK4Hccl17GlobalMirrorTasks8GetQueueEjj |
| @@ -32,6 +32,7 @@ const std::map<std::string, std::string> defaultEnvCfgMap | |||
| 32 | {"HCCL_RDMA_TIMEOUT", "6"}, | 32 | {"HCCL_RDMA_TIMEOUT", "6"}, |
| 33 | {"HCCL_RDMA_RETRY_CNT", "5"}, | 33 | {"HCCL_RDMA_RETRY_CNT", "5"}, |
| 34 | {"HCCL_RDMA_QPS_PER_CONNECTION", "1"}, | 34 | {"HCCL_RDMA_QPS_PER_CONNECTION", "1"}, |
| 35 | + {"HCCL_HOST_RDMA_UDP_PORTS_LIST", ""}, | ||
| 35 | {"HCCL_MULTI_QP_THRESHOLD", "512"}, | 36 | {"HCCL_MULTI_QP_THRESHOLD", "512"}, |
| 36 | {"HCCL_INTRA_PCIE_ENABLE", "1"}, | 37 | {"HCCL_INTRA_PCIE_ENABLE", "1"}, |
| 37 | {"HCCL_INTRA_ROCE_ENABLE", "0"}, | 38 | {"HCCL_INTRA_ROCE_ENABLE", "0"}, |
| @@ -463,6 +463,73 @@ TEST_F(EnvConfigTest, Ut_GetRdmaQueueNum_OutOfRange_ReturnsException) | |||
| 463 | unsetenv("HCCL_RDMA_QPS_PER_CONNECTION"); | 463 | unsetenv("HCCL_RDMA_QPS_PER_CONNECTION"); |
| 464 | } | 464 | } |
| 465 | 465 | ||
| 466 | +TEST_F(EnvConfigTest, Ut_EnvRdmaConfigGetHostRdmaUdpPortsList_WhenValidAndEmpty_ExpectParsed) | ||
| 467 | +{ | ||
| 468 | + setenv("HCCL_HOST_RDMA_UDP_PORTS_LIST", "0:10000,10015;1:10016,10031", 1); | ||
| 469 | + EnvRdmaConfig validRdmaConfig; | ||
| 470 | + validRdmaConfig.Parse(); | ||
| 471 | + const auto& validList = validRdmaConfig.GetHostRdmaUdpPortsList(); | ||
| 472 | + const auto device0Iter = validList.portsByPhyId.find(0); | ||
| 473 | + ASSERT_NE(device0Iter, validList.portsByPhyId.end()); | ||
| 474 | + EXPECT_EQ(device0Iter->second, (std::vector<std::uint16_t>{10000, 10015})); | ||
| 475 | + const auto device1Iter = validList.portsByPhyId.find(1); | ||
| 476 | + ASSERT_NE(device1Iter, validList.portsByPhyId.end()); | ||
| 477 | + EXPECT_EQ(device1Iter->second, (std::vector<std::uint16_t>{10016, 10031})); | ||
| 478 | + EXPECT_EQ(validList.portsByPhyId.find(2), validList.portsByPhyId.end()); | ||
| 479 | + | ||
| 480 | + setenv("HCCL_HOST_RDMA_UDP_PORTS_LIST", "", 1); | ||
| 481 | + EnvRdmaConfig emptyRdmaConfig; | ||
| 482 | + emptyRdmaConfig.Parse(); | ||
| 483 | + EXPECT_FALSE(emptyRdmaConfig.GetHostRdmaUdpPortsList().IsAvailable()); | ||
| 484 | + unsetenv("HCCL_HOST_RDMA_UDP_PORTS_LIST"); | ||
| 485 | +} | ||
| 486 | + | ||
| 487 | +TEST_F(EnvConfigTest, Ut_EnvRdmaConfigParse_WhenHostRdmaUdpPortsInvalid_ExpectUnavailable) | ||
| 488 | +{ | ||
| 489 | + std::string tooManyPorts = "0:1"; | ||
| 490 | + for (u32 i = 0; i < MultiQpSrcPortConfig::CONFIG_SRC_PORT_NUM_MAX; ++i) { | ||
| 491 | + tooManyPorts += ",1"; | ||
| 492 | + } | ||
| 493 | + const std::vector<std::string> invalidValues | ||
| 494 | + = {"a:10000", "0:0", "0:65536", "0:10000,", "0", ":10000", "0:", "0:10000:10001", tooManyPorts}; | ||
| 495 | + for (const auto& value : invalidValues) { | ||
| 496 | + SCOPED_TRACE(value); | ||
| 497 | + setenv("HCCL_HOST_RDMA_UDP_PORTS_LIST", value.c_str(), 1); | ||
| 498 | + EnvRdmaConfig rdmaConfig; | ||
| 499 | + EXPECT_NO_THROW(rdmaConfig.Parse()); | ||
| 500 | + EXPECT_FALSE(rdmaConfig.GetHostRdmaUdpPortsList().IsAvailable()); | ||
| 501 | + } | ||
| 502 | + unsetenv("HCCL_HOST_RDMA_UDP_PORTS_LIST"); | ||
| 503 | +} | ||
| 504 | + | ||
| 505 | +TEST_F(EnvConfigTest, Ut_EnvRdmaConfigParse_WhenHostRdmaUdpPortsPhyIdDuplicated_ExpectUnavailable) | ||
| 506 | +{ | ||
| 507 | + setenv("HCCL_HOST_RDMA_UDP_PORTS_LIST", "0:10000,10001;0:10002", 1); | ||
| 508 | + EnvRdmaConfig rdmaConfig; | ||
| 509 | + EXPECT_NO_THROW(rdmaConfig.Parse()); | ||
| 510 | + EXPECT_FALSE(rdmaConfig.GetHostRdmaUdpPortsList().IsAvailable()); | ||
| 511 | + unsetenv("HCCL_HOST_RDMA_UDP_PORTS_LIST"); | ||
| 512 | +} | ||
| 513 | + | ||
| 514 | +TEST_F(EnvConfigTest, Ut_EnvRdmaConfigParse_WhenHostRdmaUdpPortsPartiallyInvalid_ExpectUnavailable) | ||
| 515 | +{ | ||
| 516 | + setenv("HCCL_HOST_RDMA_UDP_PORTS_LIST", "0:10000,10001;1:0", 1); | ||
| 517 | + EnvRdmaConfig rdmaConfig; | ||
| 518 | + EXPECT_NO_THROW(rdmaConfig.Parse()); | ||
| 519 | + EXPECT_FALSE(rdmaConfig.GetHostRdmaUdpPortsList().IsAvailable()); | ||
| 520 | + unsetenv("HCCL_HOST_RDMA_UDP_PORTS_LIST"); | ||
| 521 | +} | ||
| 522 | + | ||
| 523 | +TEST_F(EnvConfigTest, Ut_EnvRdmaConfigParse_WhenHostRdmaUdpPortsValueTooLong_ExpectUnavailable) | ||
| 524 | +{ | ||
| 525 | + const std::string tooLongValue(HostRdmaUdpPortsList::CONFIG_VALUE_LEN_MAX + 1U, '0'); | ||
| 526 | + setenv("HCCL_HOST_RDMA_UDP_PORTS_LIST", tooLongValue.c_str(), 1); | ||
| 527 | + EnvRdmaConfig rdmaConfig; | ||
| 528 | + EXPECT_NO_THROW(rdmaConfig.Parse()); | ||
| 529 | + EXPECT_FALSE(rdmaConfig.GetHostRdmaUdpPortsList().IsAvailable()); | ||
| 530 | + unsetenv("HCCL_HOST_RDMA_UDP_PORTS_LIST"); | ||
| 531 | +} | ||
| 532 | + | ||
| 466 | TEST_F(EnvConfigTest, Ut_GetRdmaMultiQpThreshold_ValidValue_ReturnsCorrectValue) | 533 | TEST_F(EnvConfigTest, Ut_GetRdmaMultiQpThreshold_ValidValue_ReturnsCorrectValue) |
| 467 | { | 534 | { |
| 468 | setenv("HCCL_MULTI_QP_THRESHOLD", "1123", 1); | 535 | setenv("HCCL_MULTI_QP_THRESHOLD", "1123", 1); |
| @@ -27,6 +27,7 @@ | |||
| 27 | #include "hccl/hccl_types.h" | 27 | #include "hccl/hccl_types.h" |
| 28 | #include "hccp.h" | 28 | #include "hccp.h" |
| 29 | #include "my_rank.h" | 29 | #include "my_rank.h" |
| 30 | +#include "roce_channel_desc_configurator.h" | ||
| 30 | #include "aiv_urma_channel.h" | 31 | #include "aiv_urma_channel.h" |
| 31 | #include "channel_process.h" | 32 | #include "channel_process.h" |
| 32 | #include "coll_comm_res_c_adpt.h" | 33 | #include "coll_comm_res_c_adpt.h" |
| @@ -114,7 +115,7 @@ int StubRaGetHostMultiQpCount(RaInfo* info, HccnCfgKey key, char* value, unsigne | |||
| 114 | } | 115 | } |
| 115 | EXPECT_EQ(info->mode, NETWORK_PEER_ONLINE); | 116 | EXPECT_EQ(info->mode, NETWORK_PEER_ONLINE); |
| 116 | EXPECT_EQ(info->phyId, g_hostConfigDevicePhyId); | 117 | EXPECT_EQ(info->phyId, g_hostConfigDevicePhyId); |
| 117 | - EXPECT_EQ(*valueLen, MyRankUtils::HOST_NIC_CONFIG_BUFFER_SIZE); | 118 | + EXPECT_EQ(*valueLen, RoceChannelDescConfigurator::HOST_NIC_CONFIG_BUFFER_SIZE); |
| 118 | g_hostConfigReadKeys.emplace_back(key); | 119 | g_hostConfigReadKeys.emplace_back(key); |
| 119 | 120 | ||
| 120 | const std::string* configValue = nullptr; | 121 | const std::string* configValue = nullptr; |


[LOW] CastHostRdmaUdpPortsList 的 while (start <= s.size()) 循环与同文件刚修复的 ParseHostRdmaUdpPorts 风格不一致
CastHostRdmaUdpPortsList(line 98)使用 while (start <= s.size()) 循环,当输入以分号结尾时(如 "0:10000;")会产生一次额外迭代,依赖 deviceConfig.empty() 检查兜底。同文件刚按检视意见修复的 ParseHostRdmaUdpPorts(line 56)已改为 while(true) + find + break 模式并显式校验尾逗号。两个函数都在本 PR 中新增,逻辑高度相似(分号/逗号分隔解析),建议 CastHostRdmaUdpPortsList 也统一为 while(true) + find + break 模式,或在入口添加 s.back() == ';' 的显式校验,保持风格一致、语义清晰。