* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* ubs-io is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "bio_client_net.h"
#include <linux/version.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <cerrno>
#include <thread>
#include <utility>
#include "bio_client_agent.h"
#include "bio_client_log.h"
#include "message.h"
#include "message_op.h"
using namespace ock::bio;
using namespace ock::bio::agent;
using namespace ock::bio::net;
static void Log(int level, const char *msg)
{
if (level == 0) {
CLIENT_LOG_TRACE(msg);
} else if (level == 1U) {
CLIENT_LOG_DEBUG(msg);
} else if (level == 2U) {
CLIENT_LOG_INFO(msg);
} else if (level == 3U) {
CLIENT_LOG_WARN(msg);
} else if (level == 4U) {
CLIENT_LOG_ERROR(msg);
}
}
BResult BioClientNet::StartPre(WorkerMode mode, const NetOptions netConf)
{
mMode = mode;
BResult ret = BIO_OK;
if (mode == CONVERGENCE) {
mNetEngine = BioClientAgent::Instance()->GetNetService();
if (mNetEngine == nullptr) {
ret = BIO_INNER_ERR;
}
} else {
ret = StartIpcService(netConf);
}
return ret;
}
BResult BioClientNet::StartPost(uint16_t localNid, std::map<CmNodeId, CmNodeInfo, CmNodeIdCmp> nodeView,
uint16_t protocol, const NetOptions netConf)
{
if (mMode == CONVERGENCE) {
return BIO_OK;
}
mLocalNid = localNid;
mNetEngine->SetLocalNodeId(localNid);
std::string ipMask;
uint16_t port = UINT16_MAX;
for (auto &node : nodeView) {
if (node.second.id.VNodeId() == localNid) {
ipMask = node.second.ip;
port = node.second.port;
break;
}
}
if (port == UINT16_MAX) {
CLIENT_LOG_ERROR("Not found local node info.");
return BIO_OK;
}
auto ret = StartRpcService((ipMask + "/24"), port, static_cast<ServiceProtocol>(protocol), NO_4, netConf);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Bio client start rpc service failed, result:" << ret << ".");
return ret;
}
for (auto &node : nodeView) {
if (node.second.id.VNodeId() == localNid) {
continue;
}
if (node.second.status != CM_NODE_NORMAL) {
continue;
}
ConnectInfo info(localNid, static_cast<uint32_t>(getpid()), node.second.id.VNodeId(), node.second.ip,
node.second.port, NO_1);
CLIENT_LOG_INFO("Connect to remote node:" << info.peerId.nid << ", ip:" << info.ip << ", port:" << info.port);
BIO_TP_START(SDK_BIO_NET_START_CONNECT_FAIL, &ret, BIO_INNER_ERR);
ret = mNetEngine->SyncConnect(info);
BIO_TP_END;
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Connect to bio server failed, result:" << ret << ".");
return ret;
}
}
return BIO_OK;
}
void BioClientNet::Exit()
{
if (mMode == SEPARATES) {
StopInner();
}
}
BResult BioClientNet::CorrectFd()
{
if (mShmFd == -1) {
return BIO_OK;
}
int32_t realFd = -1;
auto result = mNetEngine->ReceiveFds(INVALID_NID, &realFd, 1U);
if (result != BIO_OK) {
CLIENT_LOG_ERROR("receive file mem fd failed, ret:" << result << ".");
return BIO_ERR;
}
mShmFd = realFd;
return BIO_OK;
}
BResult BioClientNet::CheckShmFd()
{
struct stat buffer {
};
auto ret = fstat(mShmFd, &buffer);
if (ret < 0) {
CLIENT_LOG_ERROR("Read file failed, ret:" << strerror(errno) << ".");
return BIO_ERR;
}
if (mShmOffset + mShmLength > static_cast<uint64_t>(buffer.st_size)) {
CLIENT_LOG_ERROR("Check file size failed.");
return BIO_ERR;
}
return BIO_OK;
}
BResult BioClientNet::ShmInitInner()
{
if (mShmFd == -1) {
CLIENT_LOG_INFO("mShmFd is -1,not need ShmInitInner.");
return BIO_OK;
}
if (CheckShmFd() != BIO_OK) {
mShmFd = -1;
return BIO_ERR;
}
auto offset = static_cast<off_t>(mShmOffset);
auto address = mmap(nullptr, mShmLength, PROT_READ | PROT_WRITE, MAP_SHARED, mShmFd, offset);
if (address == MAP_FAILED) {
NET_LOG_ERROR("Mmap bio_shm size " << mShmLength << " offset " << offset
<< " failed, error:" << strerror(errno));
close(mShmFd);
mShmFd = -1;
return BIO_ERR;
}
mShmAddr = static_cast<uint8_t *>(address);
return BIO_OK;
}
BResult BioClientNet::ShmInit()
{
uint64_t defaultMaxShmSize = (300UL * 1024UL * 1024UL * 1024UL);
ShmInitRequest req = {{MESSAGE_MAGIC, 0, 0, INVALID_NID, getpid()}};
ShmInitResponse rsp;
BResult ret = mNetEngine->SyncCall<ShmInitRequest, ShmInitResponse>(INVALID_NID, BIO_OP_SDK_SHM_INIT, req, rsp);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Send shm init request failed, ret:" << ret << ".");
return ret;
}
if (!CheckShmInitResp(rsp)) {
CLIENT_LOG_ERROR("Invalid responses param, alignSize:" << rsp.alignSize << ", ioTimeOut:" << rsp.ioTimeOut
<< ", netTimeOut: " << rsp.ioTimeOut << ", netTimeOut:"
<< rsp.netTimeOut << ", logLevel:" << rsp.logLevel
<< ", scene:" << rsp.scene << ".");
return BIO_INNER_ERR;
}
mShmFd = rsp.memFd;
mServerPid = rsp.serverPid;
mShmOffset = rsp.offset;
mShmLength = rsp.length;
mShmKey = rsp.mKey;
mWorkScene = rsp.scene;
mWorkIoAlignSize = rsp.alignSize;
mWorkIoTimeOut = rsp.ioTimeOut;
mWorkNetTimeOut = rsp.netTimeOut;
mLogLevel = rsp.logLevel;
mEnableCrc = rsp.enableCrc;
mEnableCli = rsp.enableCli;
mEnablePrometheus = rsp.enablePrometheus;
rsp.listenAddress[MAX_LISTEN_ADDRESS_LENGTH - 1] = '\0';
mPrometheusListenAddress = rsp.listenAddress;
mPrometheusScrapeIntervalSec = rsp.scrapeIntervalSec;
CLIENT_LOG_INFO("Bio client, scene:" << mWorkScene << ", io alignSize:" << mWorkIoAlignSize
<< ", io timeout:" << mWorkIoTimeOut << ", net timeout:" << mWorkNetTimeOut
<< ", loglevel:" << mLogLevel << ".");
mNetEngine->UpdateTimeOut(static_cast<int16_t>(mWorkNetTimeOut));
ret = mNetEngine->UpdateChannelTimeOut(INVALID_NID);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Update channel timeout failed, ret:" << ret << ".");
return ret;
}
if (mShmFd != -1 && (mShmOffset != 0 || mShmLength > defaultMaxShmSize)) {
CLIENT_LOG_ERROR("Get share memory param failed, offset:" << mShmOffset << ", length:" << mShmLength << ".");
return BIO_ERR;
}
if (CorrectFd() != BIO_OK) {
return BIO_ERR;
}
if ((ret = ShmInitInner()) != BIO_OK) {
return ret;
}
mNetEngine->SetShmInfo(mShmFd, mShmAddr, mShmOffset, mShmLength);
CLIENT_LOG_INFO("Bio client shm init success. offset:" << mShmOffset << ", length:" << mShmLength << ".");
return BIO_OK;
}
BResult BioClientNet::StartIpcService(const NetOptions netConf)
{
mNetEngine = MakeRef<NetEngine>();
if (mNetEngine == nullptr) {
CLIENT_LOG_ERROR("Make net engine failed.");
return BIO_ALLOC_FAIL;
}
int16_t timeoutSec = NO_16;
auto ret = mNetEngine->Initialize(timeoutSec, 0, NO_1024, Log);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Net engine initialize failed, result:" << ret << ".");
return ret;
}
NetOptions netOptions;
netOptions.FillNetBaseConfigs(NO_4, NO_4, Role::NET_CLIENT, ServiceProtocol::SHM);
netOptions.FillNetTlsConfigs(netConf.enableTls, netConf.certificationPath, netConf.caCerPath, netConf.caCrlPath,
netConf.privateKeyPath, netConf.privateKeyPassword, netConf.decrypterLibPath);
ret = mNetEngine->Start(netOptions);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Start ipc service failed, result:" << ret << ".");
return ret;
}
ret = SetChannelBrokenHandler();
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Set net channel broken handler failed, result:" << ret << ".");
return ret;
}
ret = RecoverIpcService();
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Recover ipc failed, result:" << ret << ".");
}
return ret;
}
BResult BioClientNet::StartRpcService(std::string ipMask, uint16_t port, ServiceProtocol protocol, uint16_t workerNum,
const NetOptions netConf)
{
const uint64_t defaultMemorySize = (128UL * 1024UL * 1024UL);
NetOptions netOptions;
netOptions.ipMask = std::move(ipMask);
netOptions.port = port;
netOptions.role = Role::NET_CLIENT;
netOptions.isBusyLoop = false;
netOptions.memorySize = defaultMemorySize;
netOptions.protocol = protocol;
netOptions.connCount = workerNum;
netOptions.handlerCount = workerNum;
netOptions.enableTls = netConf.enableTls;
netOptions.certificationPath = netConf.certificationPath;
netOptions.caCerPath = netConf.caCerPath;
netOptions.caCrlPath = netConf.caCrlPath;
netOptions.privateKeyPath = netConf.privateKeyPath;
netOptions.privateKeyPassword = netConf.privateKeyPassword;
netOptions.decrypterLibPath = netConf.decrypterLibPath;
return mNetEngine->Start(netOptions);
}
BResult BioClientNet::SetChannelBrokenHandler()
{
auto channelBroken = [this](uint32_t nodeId, uint32_t pid) -> void {
std::thread t([this, nodeId]() {
if (nodeId == INVALID_NID) {
RecoverIpc();
} else {
RecoverRpc(nodeId);
}
});
t.detach();
};
BResult ret = mNetEngine->RegisterChannelBrokenHandler(channelBroken);
if (ret != BIO_OK) {
LOG_ERROR("Client regist channel broken handler failed, ret " << ret);
}
return ret;
}
void BioClientNet::RecoverIpc()
{
constexpr uint16_t recoverInterval = 2;
uint32_t retryCnt = 0;
BResult ret;
do {
ret = RecoverIpcService();
if (ret != BIO_OK) {
CLIENT_LOG_WARN("Delay retry connect, retry cnt:" << retryCnt++);
sleep(recoverInterval);
}
} while (ret != BIO_OK);
}
BResult BioClientNet::RecoverIpcService()
{
ConnectInfo info(INVALID_NID, static_cast<uint32_t>(getpid()), INVALID_NID);
auto ret = mNetEngine->SyncConnect(info);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Connect to local bio server failed, result:" << ret << ".");
return ret;
}
ret = ShmInit();
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Get shm info fail, result:" << ret << ".");
}
return ret;
}
void BioClientNet::StopInner()
{
mNetEngine->Stop();
}
BResult BioClientNet::Rebuild(uint16_t localNid, std::map<CmNodeId, CmNodeInfo, CmNodeIdCmp> nodeView)
{
if (mMode == CONVERGENCE) {
return BIO_OK;
}
mLocalNid = localNid;
for (auto &node : nodeView) {
if (node.second.id.VNodeId() == localNid) {
continue;
}
if (node.second.status != CM_NODE_NORMAL) {
continue;
}
ConnectInfo info(localNid, static_cast<uint32_t>(getpid()), node.second.id.VNodeId(), node.second.ip,
node.second.port, NO_1);
CLIENT_LOG_INFO("Connect to remote node:" << info.peerId.nid << ", ip:" << info.ip << ", port:" << info.port
<< ".");
auto handler = [this](uintptr_t userCtx, int32_t ret, ConnectInfo &info) -> void {
if (ret != BIO_OK) {
RecoverRpc(info.peerId.nid);
}
};
auto ret = mNetEngine->AsyncConnect(info, handler, 0);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Connect to local bio server failed, result:" << ret << ".");
return ret;
}
}
return BIO_OK;
}
void BioClientNet::RecoverRpc(uint32_t peerId)
{
if (mCheckOnLine == nullptr) {
CLIENT_LOG_WARN("Unable check node isOnline, peer id:" << peerId << ".");
return;
}
std::string ip;
uint16_t port;
if (!mCheckOnLine(peerId, ip, port)) {
CLIENT_LOG_WARN("Target peer id:" << peerId << " is offline.");
return;
}
CLIENT_LOG_INFO("Reconnect to remote node:" << peerId << ", ip:" << ip << ", port:" << port << ".");
sleep(NO_2);
ConnectInfo info(mLocalNid, static_cast<uint32_t>(getpid()), peerId, ip, port, NO_1);
auto handler = [this](uintptr_t userCtx, int32_t ret, ConnectInfo &info) -> void {
if (ret != BIO_OK) {
RecoverRpc(info.peerId.nid);
}
};
BResult result = mNetEngine->AsyncConnect(info, handler, 0);
if (result != BIO_OK) {
CLIENT_LOG_ERROR("Connect to " << info.peerId.nid << " failed, ret: " << result << ".");
}
return;
}
bool BioClientNet::CheckGetUnderFsConfigResp(GetUnderFsConfigResponse &rsp)
{
size_t underFsTypeLen = strnlen(rsp.underFsType, KEY_MAX_SIZE);
size_t nameNodeLen = strnlen(rsp.hdfsConfig.nameNode, KEY_MAX_SIZE);
size_t workingPathLen = strnlen(rsp.hdfsConfig.workingPath, KEY_MAX_SIZE);
size_t userLen = strnlen(rsp.cephConfig.user, KEY_MAX_SIZE);
size_t clusterLen = strnlen(rsp.cephConfig.cluster, KEY_MAX_SIZE);
size_t cfgPathLen = strnlen(rsp.cephConfig.cfgPath, KEY_MAX_SIZE);
size_t poolLen = strnlen(rsp.cephConfig.pool, KEY_MAX_SIZE);
return ((underFsTypeLen != 0 && underFsTypeLen < KEY_MAX_SIZE) &&
(nameNodeLen != 0 && nameNodeLen < KEY_MAX_SIZE) &&
(workingPathLen != 0 && workingPathLen < KEY_MAX_SIZE) && (userLen != 0 && userLen < KEY_MAX_SIZE) &&
(clusterLen != 0 && clusterLen < KEY_MAX_SIZE) && (cfgPathLen != 0 && cfgPathLen < KEY_MAX_SIZE) &&
(poolLen != 0 && poolLen < KEY_MAX_SIZE) &&
((strcmp(rsp.underFsType, "hdfs") == 0) || (strcmp(rsp.underFsType, "ceph") == 0)));
}
BResult BioClientNet::GetUnderFsConfig(BioConfig::UnderFsConfig &config)
{
GetUnderFsConfigRequest req = {{MESSAGE_MAGIC, 0, 0, 0, getpid()}};
GetUnderFsConfigResponse rsp;
BIO_TP_START(SDK_CLIENT_GET_UNDERFS_CONFIG_PASS_SYNC_CALL, 0);
BResult ret = mNetEngine->SyncCall<GetUnderFsConfigRequest, GetUnderFsConfigResponse>(
INVALID_NID, BIO_OP_SDK_GET_UFS_CONFIG, req, rsp);
if (ret != BIO_OK) {
CLIENT_LOG_ERROR("Send get underfs configs request failed, ret:" << ret << ".");
return ret;
}
BIO_TP_END;
if (!CheckGetUnderFsConfigResp(rsp)) {
CLIENT_LOG_ERROR("Check underfs configs failed.");
return BIO_INNER_ERR;
}
config.underFsType = rsp.underFsType;
config.hdfsConfig.nameNode = rsp.hdfsConfig.nameNode;
config.hdfsConfig.workingPath = rsp.hdfsConfig.workingPath;
config.cephConfig.user = rsp.cephConfig.user;
config.cephConfig.cluster = rsp.cephConfig.cluster;
config.cephConfig.cfgPath = rsp.cephConfig.cfgPath;
config.cephConfig.pools.insert({0, rsp.cephConfig.pool});
return BIO_OK;
}
bool BioClientNet::CheckShmInitResp(ShmInitResponse rsp)
{
bool validAlignSize = rsp.alignSize <= NO_4194304 && rsp.alignSize >= NO_1;
bool validIoTimeOut = rsp.ioTimeOut >= NO_60 && rsp.ioTimeOut <= NO_300;
bool validNetTimeOut = rsp.netTimeOut >= NO_16 && rsp.netTimeOut <= NO_128;
bool validLogLevel = rsp.logLevel <= NO_4;
bool validScene = rsp.scene <= NO_1;
return validAlignSize && validIoTimeOut && validNetTimeOut && validLogLevel && validScene;
}