LCOV - code coverage report
Current view: top level - base_comm/primitives/api_c_adpt/nic_plugin - nic_plugin_manager.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 52.3 % 218 114
Test Date: 2026-07-28 12:11:00 Functions: 75.9 % 29 22

            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 "nic_plugin_manager.h"
      12              : 
      13              : #include <acl/acl_rt.h>
      14              : #include <dirent.h>
      15              : #include <dlfcn.h>
      16              : #include <algorithm>
      17              : #include <cstddef>
      18              : #include <cstdlib>
      19              : #include <cstring>
      20              : #include <memory>
      21              : #include <mutex>
      22              : #include <string>
      23              : #include <unordered_map>
      24              : #include <vector>
      25              : 
      26              : #include "hcomm_result_defs.h"
      27              : #include "log.h"
      28              : #include "param_check_pub.h"
      29              : 
      30              : namespace hcomm {
      31              : namespace {
      32              : constexpr const char *HCOMM_NIC_PLUGIN_DIR = "hcomm_plugin";
      33              : constexpr const char *HCOMM_NIC_PLUGIN_SO_ENV = "HCOMM_NIC_PLUGIN_SO";
      34              : 
      35            9 : std::once_flag &LoadOnce()
      36              : {
      37              :     static std::once_flag loadOnce;
      38            9 :     return loadOnce;
      39              : }
      40              : 
      41            0 : std::vector<std::unique_ptr<NicPluginEntry>> &LoadedPlugins()
      42              : {
      43            0 :     static std::vector<std::unique_ptr<NicPluginEntry>> loadedPlugins;
      44            0 :     return loadedPlugins;
      45              : }
      46              : 
      47            9 : std::unordered_map<CommProtocol, const NicPluginEntry *> &ProtocolPlugins()
      48              : {
      49            9 :     static std::unordered_map<CommProtocol, const NicPluginEntry *> protocolPlugins;
      50            9 :     return protocolPlugins;
      51              : }
      52              : 
      53            0 : bool EndsWithSo(const std::string &path)
      54              : {
      55            0 :     constexpr const char *suffix = ".so";
      56            0 :     constexpr size_t suffixLen = 3U;
      57            0 :     return path.size() >= suffixLen && path.compare(path.size() - suffixLen, suffixLen, suffix) == 0;
      58              : }
      59              : 
      60           44 : bool IsOpsHeaderValid(const CommAbiHeader &header, uint32_t magicWord, uint32_t version, const char *opsName)
      61              : {
      62           44 :     if (header.magicWord != magicWord) {
      63            1 :         HCCL_RUN_WARNING("[NicPlugin] %s magicWord[0x%08x] mismatch, expected[0x%08x].",
      64              :             opsName, header.magicWord, magicWord);
      65            1 :         return false;
      66              :     }
      67           43 :     if (header.version != version) {
      68            1 :         HCCL_RUN_WARNING("[NicPlugin] %s version[%u] mismatch, expected[%u].", opsName, header.version, version);
      69            1 :         return false;
      70              :     }
      71           42 :     if (header.size < sizeof(CommAbiHeader)) {
      72            0 :         HCCL_RUN_WARNING("[NicPlugin] %s size[%u] is smaller than ABI header[%zu].",
      73              :             opsName, header.size, sizeof(CommAbiHeader));
      74            0 :         return false;
      75              :     }
      76           42 :     return true;
      77              : }
      78              : 
      79            9 : bool ValidateEndpointOps(const HcommNicEndpointOps *ops)
      80              : {
      81            9 :     return ops != nullptr && IsOpsHeaderValid(ops->header, HCOMM_NIC_ENDPOINT_OPS_MAGIC_WORD,
      82            9 :         HCOMM_NIC_ENDPOINT_OPS_VERSION, "endpoint ops");
      83              : }
      84              : 
      85           31 : bool ValidateChannelOps(const HcommNicChannelOps *ops)
      86              : {
      87           31 :     return ops != nullptr && IsOpsHeaderValid(ops->header, HCOMM_NIC_CHANNEL_OPS_MAGIC_WORD,
      88           31 :         HCOMM_NIC_CHANNEL_OPS_VERSION, "channel ops");
      89              : }
      90              : 
      91              : template <typename PluginOps>
      92           44 : bool IsPluginOpAvailable(const PluginOps *ops, size_t opOffset, size_t opSize)
      93              : {
      94           44 :     return ops != nullptr && ops->header.size >= opOffset + opSize;
      95              : }
      96              : 
      97            0 : void RegisterPluginProtocols(const NicPluginEntry *plugin)
      98              : {
      99            0 :     auto &protocolPlugins = ProtocolPlugins();
     100            0 :     for (uint32_t idx = 0; idx < plugin->info->protocolCount; ++idx) {
     101            0 :         const CommProtocol protocol = plugin->info->protocols[idx];
     102            0 :         auto iter = protocolPlugins.find(protocol);
     103            0 :         if (iter != protocolPlugins.end()) {
     104            0 :             HCCL_RUN_WARNING("[NicPlugin] protocol[%d] handler[%s] is overwritten by plugin[%s].",
     105              :                 protocol,
     106              :                 iter->second->info->name == nullptr ? "unknown" : iter->second->info->name,
     107              :                 plugin->info->name == nullptr ? "unknown" : plugin->info->name);
     108              :         }
     109            0 :         protocolPlugins[protocol] = plugin;
     110            0 :         HCCL_RUN_INFO("[NicPlugin] protocol[%d] is handled by plugin[%s].",
     111              :             protocol, plugin->info->name == nullptr ? "unknown" : plugin->info->name);
     112              :     }
     113            0 : }
     114              : 
     115            0 : void *LoadSymbol(void *soHandle, const char *soPath, const char *symbol)
     116              : {
     117            0 :     dlerror();
     118            0 :     void *addr = dlsym(soHandle, symbol);
     119            0 :     const char *dlsymErr = dlerror();
     120            0 :     if (dlsymErr != nullptr || addr == nullptr) {
     121            0 :         HCCL_RUN_WARNING("[NicPlugin] dlsym %s from %s failed: %s.",
     122              :             symbol, soPath, dlsymErr == nullptr ? "unknown" : dlsymErr);
     123            0 :         return nullptr;
     124              :     }
     125            0 :     return addr;
     126              : }
     127              : 
     128            0 : void LoadOnePlugin(const std::string &path)
     129              : {
     130            0 :     if (path.empty()) {
     131            0 :         return;
     132              :     }
     133            0 :     void *soHandle = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
     134            0 :     if (soHandle == nullptr) {
     135            0 :         HCCL_RUN_WARNING("[NicPlugin] dlopen %s failed: %s.", path.c_str(), dlerror());
     136            0 :         return;
     137              :     }
     138              : 
     139              :     auto getInfo = reinterpret_cast<HcommNicPluginGetInfoFunc>(
     140            0 :         LoadSymbol(soHandle, path.c_str(), "HcommNicPluginGetInfo"));
     141              :     auto createEndpoint = reinterpret_cast<HcommNicPluginCreateEndpointFunc>(
     142            0 :         LoadSymbol(soHandle, path.c_str(), "HcommNicPluginCreateEndpoint"));
     143              :     auto createChannel = reinterpret_cast<HcommNicPluginCreateChannelFunc>(
     144            0 :         LoadSymbol(soHandle, path.c_str(), "HcommNicPluginCreateChannel"));
     145            0 :     if (getInfo == nullptr || createEndpoint == nullptr || createChannel == nullptr) {
     146            0 :         dlclose(soHandle);
     147            0 :         return;
     148              :     }
     149              : 
     150            0 :     const HcommNicPluginInfo *info = getInfo();
     151            0 :     if (!ValidatePluginInfo(path.c_str(), info, createEndpoint, createChannel)) {
     152            0 :         dlclose(soHandle);
     153            0 :         return;
     154              :     }
     155              : 
     156              :     std::unique_ptr<NicPluginEntry> plugin(new (std::nothrow) NicPluginEntry{soHandle, info, createEndpoint,
     157            0 :         createChannel});
     158            0 :     if (plugin == nullptr) {
     159            0 :         HCCL_RUN_WARNING("[NicPlugin] allocate plugin entry for %s failed.", path.c_str());
     160            0 :         dlclose(soHandle);
     161            0 :         return;
     162              :     }
     163            0 :     RegisterPluginProtocols(plugin.get());
     164            0 :     LoadedPlugins().emplace_back(std::move(plugin));
     165            0 : }
     166              : 
     167            0 : void LoadDefaultDirectory(const std::string &pluginDir)
     168              : {
     169            0 :     DIR *dir = opendir(pluginDir.c_str());
     170            0 :     if (dir == nullptr) {
     171            0 :         HCCL_RUN_INFO("[NicPlugin] plugin directory %s is unavailable.", pluginDir.c_str());
     172            0 :         return;
     173              :     }
     174            0 :     std::vector<std::string> soPaths;
     175            0 :     for (dirent *entry = readdir(dir); entry != nullptr; entry = readdir(dir)) {
     176            0 :         const std::string name(entry->d_name);
     177            0 :         if (name == "." || name == ".." || !EndsWithSo(name)) {
     178            0 :             continue;
     179              :         }
     180            0 :         soPaths.emplace_back(pluginDir + "/" + name);
     181            0 :     }
     182            0 :     closedir(dir);
     183            0 :     std::sort(soPaths.begin(), soPaths.end());
     184            0 :     for (const auto &path : soPaths) {
     185            0 :         LoadOnePlugin(path);
     186              :     }
     187            0 : }
     188              : 
     189            0 : void LoadExplicitPlugins(const char *envValue)
     190              : {
     191            0 :     if (envValue == nullptr || envValue[0] == '\0') {
     192            0 :         return;
     193              :     }
     194            0 :     const std::string paths(envValue);
     195            0 :     size_t start = 0;
     196            0 :     while (start <= paths.size()) {
     197            0 :         const size_t end = paths.find(':', start);
     198            0 :         const std::string path = paths.substr(start, end == std::string::npos ? std::string::npos : end - start);
     199            0 :         LoadOnePlugin(path);
     200            0 :         if (end == std::string::npos) {
     201            0 :             break;
     202              :         }
     203            0 :         start = end + 1;
     204            0 :     }
     205            0 : }
     206              : 
     207            1 : void LoadPluginsOnce()
     208              : {
     209            1 :     uint32_t deviceCount = 0;
     210            1 :     const aclError ret = aclrtGetDeviceCount(&deviceCount);
     211            1 :     if (ret == ACL_SUCCESS && deviceCount != 0) {
     212            1 :         HCCL_RUN_INFO("[NicPlugin] plugin loading skipped, aclrtGetDeviceCount ret[%d], count[%u].",
     213              :             ret, deviceCount);
     214            1 :         return;
     215              :     }
     216              : 
     217            0 :     const char *ascendHomePath = getenv("ASCEND_HOME_PATH");
     218            0 :     if (ascendHomePath != nullptr && ascendHomePath[0] != '\0') {
     219            0 :         LoadDefaultDirectory(std::string(ascendHomePath) + "/" + HCOMM_NIC_PLUGIN_DIR);
     220              :     } else {
     221            0 :         HCCL_RUN_INFO("[NicPlugin] ASCEND_HOME_PATH is empty, skip default plugin directory.");
     222            0 :         LoadExplicitPlugins(getenv(HCOMM_NIC_PLUGIN_SO_ENV));
     223              :     }
     224              : }
     225              : 
     226              : template <typename PluginOps>
     227            4 : void DestroyPluginCtx(PluginOps *ops, void *pluginCtx)
     228              : {
     229            8 :     if (ops != nullptr && IsPluginOpAvailable(ops, offsetof(PluginOps, destroy), sizeof(ops->destroy)) &&
     230            4 :         ops->destroy != nullptr) {
     231            4 :         ops->destroy(pluginCtx);
     232              :     }
     233            4 : }
     234              : 
     235              : template <typename PluginOps>
     236            4 : HcommResult InitPluginCtxOrDestroy(PluginOps *ops, void *pluginCtx)
     237              : {
     238            4 :     CHK_PTR_NULL(pluginCtx);
     239            4 :     CHK_PTR_NULL(ops);
     240            4 :     if (!IsPluginOpAvailable(ops, offsetof(PluginOps, init), sizeof(ops->init)) || ops->init == nullptr) {
     241            3 :         return HCCL_SUCCESS;
     242              :     }
     243            1 :     HcommResult ret = ops->init(pluginCtx);
     244            1 :     if (ret != HCCL_SUCCESS) {
     245            0 :         DestroyPluginCtx(ops, pluginCtx);
     246              :     }
     247            1 :     return ret;
     248              : }
     249              : } // namespace
     250              : 
     251            4 : bool ValidatePluginInfo(const char *soPath, const HcommNicPluginInfo *info,
     252              :     HcommNicPluginCreateEndpointFunc createEndpoint, HcommNicPluginCreateChannelFunc createChannel)
     253              : {
     254            4 :     if (info == nullptr) {
     255            0 :         HCCL_RUN_WARNING("[NicPlugin] %s exports null plugin info.", soPath);
     256            0 :         return false;
     257              :     }
     258            4 :     if (!IsOpsHeaderValid(info->header, HCOMM_NIC_PLUGIN_INFO_MAGIC_WORD,
     259              :         HCOMM_NIC_PLUGIN_INFO_VERSION, "plugin info")) {
     260            2 :         return false;
     261              :     }
     262            2 :     constexpr size_t requiredSize = offsetof(HcommNicPluginInfo, protocols) +
     263              :         sizeof(static_cast<HcommNicPluginInfo *>(nullptr)->protocols);
     264            2 :     if (info->header.size < requiredSize) {
     265            1 :         HCCL_RUN_WARNING("[NicPlugin] %s plugin info size[%u] is smaller than required[%zu].",
     266              :             soPath, info->header.size, requiredSize);
     267            1 :         return false;
     268              :     }
     269            1 :     if (info->protocolCount == 0 || info->protocolCount > HCOMM_NIC_PLUGIN_MAX_PROTOCOLS) {
     270            0 :         HCCL_RUN_WARNING("[NicPlugin] %s invalid protocolCount[%u].", soPath, info->protocolCount);
     271            0 :         return false;
     272              :     }
     273            1 :     if (createEndpoint == nullptr || createChannel == nullptr) {
     274            0 :         HCCL_RUN_WARNING("[NicPlugin] %s missing create endpoint/channel symbol.", soPath);
     275            0 :         return false;
     276              :     }
     277            2 :     for (uint32_t idx = 0; idx < info->protocolCount; ++idx) {
     278            1 :         const CommProtocol protocol = info->protocols[idx];
     279            1 :         if (protocol < COMM_PROTOCOL_HCCS || protocol > COMM_PROTOCOL_HCCS_ONLY) {
     280            0 :             HCCL_RUN_WARNING("[NicPlugin] %s invalid protocol[%d].", soPath, info->protocols[idx]);
     281            0 :             return false;
     282              :         }
     283              :     }
     284            1 :     return true;
     285              : }
     286              : 
     287            9 : void LoadAllNicPlugins()
     288              : {
     289            9 :     std::call_once(LoadOnce(), LoadPluginsOnce);
     290            9 : }
     291              : 
     292            9 : const NicPluginEntry *FindHostNicPlugin(CommProtocol protocol)
     293              : {
     294            9 :     LoadAllNicPlugins();
     295            9 :     const auto &protocolPlugins = ProtocolPlugins();
     296            9 :     auto iter = protocolPlugins.find(protocol);
     297            9 :     const NicPluginEntry *entry = iter == protocolPlugins.end() ? nullptr : iter->second;
     298            9 :     return entry;
     299              : }
     300              : 
     301            1 : HcommResult CreatePluginEndpoint(const EndpointDesc *endpoint, EndpointHandle *endpointHandle)
     302              : {
     303            1 :     CHK_PTR_NULL(endpoint);
     304            1 :     CHK_PTR_NULL(endpointHandle);
     305            1 :     const NicPluginEntry *entry = FindHostNicPlugin(endpoint->protocol);
     306            1 :     if (entry == nullptr) {
     307            0 :         return HCCL_E_NOT_FOUND;
     308              :     }
     309            1 :     void *pluginCtx = nullptr;
     310            1 :     HcommNicEndpointOps *ops = nullptr;
     311            1 :     CHK_RET(static_cast<HcclResult>(entry->createEndpoint(endpoint, &pluginCtx, &ops)));
     312            1 :     CHK_PRT_RET(!ValidateEndpointOps(ops), HCCL_ERROR("[NicPlugin] invalid endpoint ops."), HCCL_E_PARA);
     313            1 :     HcommResult ret = InitPluginCtxOrDestroy(ops, pluginCtx);
     314            1 :     if (ret != HCCL_SUCCESS) {
     315            0 :         return ret;
     316              :     }
     317            1 :     PluginEndpointCtx *ctx = new (std::nothrow) PluginEndpointCtx{ops, pluginCtx, entry};
     318            1 :     if (ctx == nullptr) {
     319            0 :         DestroyPluginCtx(ops, pluginCtx);
     320            0 :         return HCCL_E_MEMORY;
     321              :     }
     322            1 :     *endpointHandle = MAKE_PLUGIN_EP_HANDLE(ctx);
     323            1 :     return HCCL_SUCCESS;
     324              : }
     325              : 
     326            1 : HcommResult DestroyPluginEndpoint(EndpointHandle endpointHandle)
     327              : {
     328            1 :     PluginEndpointCtx *ctx = PLUGIN_EP_CTX(endpointHandle);
     329            1 :     CHK_PTR_NULL(ctx);
     330            1 :     DestroyPluginCtx(ctx->ops, ctx->ctx);
     331            1 :     delete ctx;
     332            1 :     return HCCL_SUCCESS;
     333              : }
     334              : 
     335            3 : HcommResult CreatePluginChannel(EndpointHandle endpointHandle, const HcommChannelDesc *channelDesc,
     336              :     ChannelHandle *channelHandle)
     337              : {
     338            3 :     PluginEndpointCtx *endpointCtx = PLUGIN_EP_CTX(endpointHandle);
     339            3 :     CHK_PTR_NULL(endpointCtx);
     340            3 :     CHK_PTR_NULL(endpointCtx->entry);
     341            3 :     CHK_PTR_NULL(channelDesc);
     342            3 :     CHK_PTR_NULL(channelHandle);
     343            3 :     void *pluginCtx = nullptr;
     344            3 :     HcommNicChannelOps *ops = nullptr;
     345            3 :     CHK_RET(static_cast<HcclResult>(
     346              :         endpointCtx->entry->createChannel(endpointCtx->ctx, channelDesc, &pluginCtx, &ops)));
     347            3 :     CHK_PRT_RET(!ValidateChannelOps(ops), HCCL_ERROR("[NicPlugin] invalid channel ops."), HCCL_E_PARA);
     348            3 :     HcommResult ret = InitPluginCtxOrDestroy(ops, pluginCtx);
     349            3 :     if (ret != HCCL_SUCCESS) {
     350            0 :         return ret;
     351              :     }
     352            3 :     PluginChannelCtx *ctx = new (std::nothrow) PluginChannelCtx{ops, pluginCtx, endpointCtx->entry};
     353            3 :     if (ctx == nullptr) {
     354            0 :         DestroyPluginCtx(ops, pluginCtx);
     355            0 :         return HCCL_E_MEMORY;
     356              :     }
     357            3 :     *channelHandle = MAKE_PLUGIN_CH_HANDLE(ctx);
     358            3 :     return HCCL_SUCCESS;
     359              : }
     360              : 
     361            3 : HcommResult DestroyPluginChannel(ChannelHandle channelHandle)
     362              : {
     363            3 :     PluginChannelCtx *ctx = PLUGIN_CH_CTX(channelHandle);
     364            3 :     CHK_PTR_NULL(ctx);
     365            3 :     DestroyPluginCtx(ctx->ops, ctx->ctx);
     366            3 :     delete ctx;
     367            3 :     return HCCL_SUCCESS;
     368              : }
     369              : 
     370           13 : HcommResult UnsupportedPluginOp(const char *opName)
     371              : {
     372           13 :     HCCL_RUN_WARNING("[NicPlugin] plugin operation[%s] is not supported.", opName == nullptr ? "unknown" : opName);
     373           13 :     return HCCL_E_NOT_SUPPORT;
     374              : }
     375              : 
     376            8 : bool IsEndpointOpAvailable(const HcommNicEndpointOps *ops, size_t opOffset, size_t opSize)
     377              : {
     378            8 :     return ValidateEndpointOps(ops) && IsPluginOpAvailable(ops, opOffset, opSize);
     379              : }
     380              : 
     381           28 : bool IsChannelOpAvailable(const HcommNicChannelOps *ops, size_t opOffset, size_t opSize)
     382              : {
     383           28 :     return ValidateChannelOps(ops) && IsPluginOpAvailable(ops, opOffset, opSize);
     384              : }
     385              : 
     386              : } // namespace hcomm
        

Generated by: LCOV version 2.0-1