* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* 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 FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#include <cinttypes>
#include <memory>
#include <mutex>
#include <new>
#include <string>
#include "api_impl_creator.hpp"
#include "api_impl_vmm.hpp"
#include "base.hpp"
#include "context.hpp"
#include "device.hpp"
#include "driver/ascend_hal.h"
#include "driver_enum_desc.hpp"
#include "enum_desc.hpp"
#include "error_message_manage.hpp"
#include "mem_type.hpp"
#include "npu_driver.hpp"
#include "runtime.hpp"
#include "utils.h"
namespace cce {
namespace runtime {
namespace {
constexpr uint32_t HUGE1G_PAGE = 2U;
constexpr uint32_t DEVICE_TYPE = 1U;
constexpr uint32_t NUMA_TYPE = 4U;
constexpr uint32_t DRV_MEM_HOST_NUMA_SIDE = 2U;
constexpr int32_t FEATURE_SVM_VMM_NORMAL_GRANULARITY = 6;
constexpr char_t MEM_SHARED_HANDLE_TYPE_EXPECT_DESC[] =
"MEM_SHARE_HANDLE_TYPE_DEFAULT(1) or MEM_SHARE_HANDLE_TYPE_FABRIC(2)";
rtError_t DoFreePhysical(rtDrvMemHandle handle)
{
RT_LOG(RT_LOG_INFO, "Start to FreePhysical");
return NpuDriver::FreePhysical(handle);
}
rtError_t DoMemRetainAllocationHandle(void* virPtr, rtDrvMemHandle* handle)
{
const rtError_t error = NpuDriver::MemRetainAllocationHandle(virPtr, handle);
ERROR_RETURN(error, "Failed to obtain the handle from virtual pointer, ptr=%p, handle=%p.", virPtr, handle);
return RT_ERROR_NONE;
}
rtError_t DoMemGetAddressRange(void* ptr, void** pbase, size_t* psize)
{
RT_LOG(RT_LOG_INFO, "Start to MemGetAddressRange");
const rtError_t error = NpuDriver::MemGetAddressRange(ptr, pbase, psize);
ERROR_RETURN(error, "Call MemGetAddressRange failed, ptr=%p", ptr);
return error;
}
rtError_t ParseMallocCfg(const rtMallocConfig_t* const cfg, rtConfigValue_t* const cfgVal)
{
for (size_t i = 0U; i < cfg->numAttrs; ++i) {
switch (cfg->attrs[i].attr) {
case RT_MEM_MALLOC_ATTR_RSV:
break;
case RT_MEM_MALLOC_ATTR_MODULE_ID:
cfgVal->moduleId = cfg->attrs[i].value.moduleId;
break;
case RT_MEM_MALLOC_ATTR_DEVICE_ID:
cfgVal->deviceId = cfg->attrs[i].value.deviceId;
break;
default:
RT_LOG(RT_LOG_ERROR, "invalid attribute %s", MallocAttrToString(cfg->attrs[i].attr));
RT_LOG_OUTER_MSG_WITH_FUNC_DESC(
ErrorCode::EE1003, "Parsing the memory allocation configuration",
MallocAttrToString(cfg->attrs[i].attr), "cfg->attrs[i].attr", "[0, 2]");
return RT_ERROR_INVALID_VALUE;
}
}
return RT_ERROR_NONE;
}
}
bool IsImplVmmSupported() { return true; }
ApiVmm* CreateImplVmmAndGet()
{
ApiVmm* const apiImplVmm = new (std::nothrow) ApiImplVmm();
if (apiImplVmm == nullptr) {
RT_LOG_OUTER_MSG_IMPL(ErrorCode::EE1013, sizeof(ApiImplVmm), "new");
RT_LOG(RT_LOG_ERROR, "create ApiImplVmm failed.");
return nullptr;
}
RT_LOG(RT_LOG_INFO, "ApiImplVmm:Runtime_alloc_size %zu", sizeof(ApiImplVmm));
return apiImplVmm;
}
void DestroyImplVmm(ApiVmm*& apiImplVmm)
{
delete apiImplVmm;
apiImplVmm = nullptr;
}
rtError_t ApiImplVmm::ReserveMemAddress(void** devPtr, size_t size, size_t alignment, void* devAddr, uint64_t flags)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(devPtr, RT_ERROR_INVALID_VALUE, "Virtual memory allocation");
Runtime::Instance()->CallApiBegin(RT_PROF_API_DEV_MALLOC);
const rtError_t error = NpuDriver::ReserveMemAddress(devPtr, size, alignment, devAddr, flags);
RT_LOG(
RT_LOG_INFO, "device malloc Succ, size=%" PRIu64 ", start ptr=%p, end ptr=%p", size, *devPtr,
RtPtrToPtr<void*>(RtPtrToPtr<uint8_t*>(*devPtr) + size));
if (error == RT_ERROR_NONE) {
Runtime::Instance()->FillRuntimeMemMngExtInfo(
RtPtrToValue(*devPtr), static_cast<uint64_t>(size), RT_PROF_MEM_MNG_TYPE_MALLOC, MSPROF_MEMORY_TYPE_DEVICE,
nullptr);
}
Runtime::Instance()->CallApiEnd(error);
return error;
}
rtError_t ApiImplVmm::ReleaseMemAddress(void* devPtr)
{
Runtime::Instance()->CallApiBegin(RT_PROF_API_DEV_FREE);
RT_LOG(RT_LOG_INFO, "device free mem=0x%llx", RtPtrToPtr<uint64_t*>(devPtr));
const rtError_t error = NpuDriver::ReleaseMemAddress(devPtr);
if (error == RT_ERROR_NONE) {
Runtime::Instance()->FillRuntimeMemMngExtInfo(
RtPtrToValue(devPtr), 0U, RT_PROF_MEM_MNG_TYPE_FREE, MSPROF_MEMORY_TYPE_DEVICE, nullptr);
}
Runtime::Instance()->CallApiEnd(error);
return error;
}
rtError_t ApiImplVmm::MallocPhysical(rtDrvMemHandle* handle, size_t size, rtDrvMemProp_t* prop, uint64_t flags)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(prop, RT_ERROR_INVALID_VALUE, "Physical memory allocation");
rtError_t error = RT_ERROR_NONE;
if (prop->side == DEVICE_TYPE) {
const uint32_t userDeviceId = prop->devid;
error = Runtime::Instance()->ChgUserDevIdToDeviceId(userDeviceId, &prop->devid);
COND_RETURN_ERROR(
error != RT_ERROR_NONE, RT_ERROR_DEVICE_ID, "Failed to convert the user device ID %u to driver device ID.",
userDeviceId);
error = Runtime::Instance()->CheckDeviceIdIsValid(static_cast<int32_t>(prop->devid));
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, error, "drv devId is invalid, drv devId=%u, retCode=%#x", prop->devid,
static_cast<uint32_t>(error));
}
if (prop->side == NUMA_TYPE) {
Context* curCtx = Runtime::Instance()->CurrentContext();
CHECK_CONTEXT_VALID_WITH_RETURN(curCtx, RT_ERROR_CONTEXT_NULL);
NULL_PTR_RETURN_MSG(curCtx->Device_(), RT_ERROR_DEVICE_NULL);
COND_RETURN_WARN(
(!(NpuDriver::CheckIsSupportFeature(curCtx->Device_()->Id_(), FEATURE_SVM_VMM_NORMAL_GRANULARITY))),
RT_ERROR_DRV_NOT_SUPPORT, "[drv api] driver does not support the alloc mem via numa id feature.");
prop->side = DRV_MEM_HOST_NUMA_SIDE;
}
if (prop->pg_type == HUGE1G_PAGE) {
error = NpuDriver::CheckIfSupport1GHugePage();
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, error, "pageType does not support, pageType[%u], retCode=%#x", prop->pg_type,
static_cast<uint32_t>(error));
}
Runtime::Instance()->CallApiBegin(RT_PROF_API_DEV_MALLOC);
RT_LOG(RT_LOG_INFO, "Start to MallocPhysical, size=%zu", size);
error = NpuDriver::MallocPhysical(handle, size, prop, flags);
if (error == RT_ERROR_NONE) {
const uint64_t addr = (handle == nullptr) ? 0U : RtPtrToValue(*handle);
Runtime::Instance()->FillRuntimeMemMngExtInfo(
addr, static_cast<uint64_t>(size), RT_PROF_MEM_MNG_TYPE_MALLOC, MSPROF_MEMORY_TYPE_DEVICE, nullptr);
}
Runtime::Instance()->CallApiEnd(error);
return error;
}
rtError_t ApiImplVmm::FreePhysical(rtDrvMemHandle handle)
{
Runtime::Instance()->CallApiBegin(RT_PROF_API_DEV_FREE);
const rtError_t error = DoFreePhysical(handle);
if (error == RT_ERROR_NONE) {
Runtime::Instance()->FillRuntimeMemMngExtInfo(
RtPtrToValue(handle), 0U, RT_PROF_MEM_MNG_TYPE_FREE, MSPROF_MEMORY_TYPE_DEVICE, nullptr);
}
Runtime::Instance()->CallApiEnd(error);
return error;
}
rtError_t ApiImplVmm::MapMem(void* devPtr, size_t size, size_t offset, rtDrvMemHandle handle, uint64_t flags)
{
const rtError_t error = NpuDriver::MapMem(devPtr, size, offset, handle, flags);
ERROR_RETURN(error, "failed, size=%" PRIu64 ", ptr=%p.", size, devPtr);
RT_LOG(
RT_LOG_INFO, "device malloc Succ, size=%" PRIu64 ", start ptr=%p, end ptr=%p", size, devPtr,
RtPtrToPtr<void*>(RtPtrToPtr<uint8_t*>(devPtr) + size));
return error;
}
rtError_t ApiImplVmm::UnmapMem(void* devPtr)
{
RT_LOG(RT_LOG_INFO, "device free mem=%p", devPtr);
const rtError_t error = NpuDriver::UnmapMem(devPtr);
ERROR_RETURN(error, "failed mem=%p", devPtr);
return error;
}
rtError_t ApiImplVmm::MemMapNoAccess(void* virPtr, size_t size, size_t offset, rtDrvMemHandle handle, uint64_t flags)
{
const rtError_t error = NpuDriver::MemMapNoAccess(virPtr, size, offset, handle, flags);
COND_RETURN_WITH_NOLOG(error == RT_ERROR_FEATURE_NOT_SUPPORT, error);
ERROR_RETURN(
error, "failed, ptr=%p, size=%zu, offset=%zu, handle=%p, flags=%" PRIu64, virPtr, size, offset, handle, flags);
return error;
}
rtError_t ApiImplVmm::MemSetAccess(void* virPtr, size_t size, rtMemAccessDesc* desc, size_t count)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(virPtr, RT_ERROR_INVALID_VALUE, "Setting the memory access permission");
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(desc, RT_ERROR_INVALID_VALUE, "Setting the memory access permission");
if (count == 0U) {
const rtError_t error = NpuDriver::MemSetAccess(virPtr, size, desc, count);
COND_RETURN_WARN(
error == RT_ERROR_FEATURE_NOT_SUPPORT, RT_ERROR_FEATURE_NOT_SUPPORT,
"failed, ptr=0x%llx, size=%" PRIu64 ", count=%" PRIu64, RtPtrToValue(virPtr), size, count);
ERROR_RETURN(error, "failed, ptr=0x%llx, size=%" PRIu64 ", count=%" PRIu64, RtPtrToValue(virPtr), size, count);
return error;
}
std::unique_ptr<rtMemAccessDesc[]> realDesc;
try {
realDesc.reset(new (std::nothrow) rtMemAccessDesc[count]);
} catch (const std::bad_array_new_length&) {
RT_LOG_OUTER_MSG_WITH_FUNC_DESC(
ErrorCode::EE1011, "Setting the memory access permission", count, "count",
"The memory access descriptor array cannot be allocated with the specified count");
return RT_ERROR_INVALID_VALUE;
}
COND_RETURN_AND_MSG_OUTER(
realDesc == nullptr, RT_ERROR_INVALID_VALUE, ErrorCode::EE1011, "Setting the memory access permission", count,
"count", "The memory access descriptor array cannot be allocated with the specified count");
for (size_t idx = 0U; idx < count; ++idx) {
realDesc[idx] = desc[idx];
if (realDesc[idx].location.type != RT_MEMORY_LOC_DEVICE) {
continue;
}
const uint32_t userDeviceId = realDesc[idx].location.id;
const rtError_t convertError =
Runtime::Instance()->ChgUserDevIdToDeviceId(userDeviceId, &realDesc[idx].location.id);
COND_RETURN_ERROR(
convertError != RT_ERROR_NONE, convertError, "Failed to convert the user device ID %u to driver device ID.",
userDeviceId);
}
const rtError_t error = NpuDriver::MemSetAccess(virPtr, size, realDesc.get(), count);
COND_RETURN_WARN(
error == RT_ERROR_FEATURE_NOT_SUPPORT, RT_ERROR_FEATURE_NOT_SUPPORT,
"failed, ptr=0x%llx, size=%" PRIu64 ", count=%" PRIu64, RtPtrToValue(virPtr), size, count);
ERROR_RETURN(error, "failed, ptr=0x%llx, size=%" PRIu64 ", count=%" PRIu64, RtPtrToValue(virPtr), size, count);
return error;
}
rtError_t ApiImplVmm::MemGetAccess(void* virPtr, rtMemLocation* location, uint64_t* flags)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(virPtr, RT_ERROR_INVALID_VALUE, "Obtaining the memory access permission");
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
location, RT_ERROR_INVALID_VALUE, "Obtaining the memory access permission");
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(flags, RT_ERROR_INVALID_VALUE, "Obtaining the memory access permission");
rtMemLocation realLocation = *location;
if (realLocation.type == RT_MEMORY_LOC_DEVICE) {
const uint32_t userDeviceId = realLocation.id;
const rtError_t convertError = Runtime::Instance()->ChgUserDevIdToDeviceId(userDeviceId, &realLocation.id);
COND_RETURN_ERROR(
convertError != RT_ERROR_NONE, convertError, "Failed to convert the user device ID %u to driver device ID.",
userDeviceId);
}
const rtError_t error = NpuDriver::MemGetAccess(virPtr, &realLocation, flags);
ERROR_RETURN(
error, "failed, ptr=%p, location=%d, flags=%#" PRIu64 ".", virPtr, static_cast<int32_t>(realLocation.type),
*flags);
return error;
}
rtError_t ApiImplVmm::ExportToShareableHandle(
rtDrvMemHandle handle, rtDrvMemHandleType handleType, uint64_t flags, uint64_t* shareableHandle)
{
constexpr uint64_t maxFlag = RT_VMM_EXPORT_FLAG_DISABLE_PID_VALIDATION;
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
(flags > maxFlag), RT_ERROR_INVALID_VALUE, "Exporting the physical memory handle of the device", flags,
"[0, " + std::to_string(maxFlag) + "]");
constexpr uint64_t drvFlags = 0UL;
rtError_t error = NpuDriver::ExportToShareableHandle(handle, handleType, drvFlags, shareableHandle);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
if ((flags & RT_VMM_EXPORT_FLAG_DISABLE_PID_VALIDATION) != 0UL) {
error = NpuDriver::SetMemShareHandleDisablePidVerify(*shareableHandle);
}
RT_LOG(
RT_LOG_INFO, "handleType=%s, flags=%#" PRIx64 ", shareableHandle=%" PRIu64 ".",
DrvMemHandleTypeToString(handleType), flags, *shareableHandle);
return error;
}
rtError_t ApiImplVmm::ExportToShareableHandleV2(
rtDrvMemHandle handle, rtMemSharedHandleType handleType, uint64_t flags, void* shareableHandle)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
shareableHandle, RT_ERROR_INVALID_VALUE, "Exporting the shared handle of the AI server");
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_NAME_AND_FUNC_DESC(
(handleType != RT_MEM_SHARE_HANDLE_TYPE_DEFAULT && handleType != RT_MEM_SHARE_HANDLE_TYPE_FABRIC),
RT_ERROR_INVALID_VALUE, "Exporting the shared handle of the AI server",
RtFmtMsg("UNKNOWN(%d)", static_cast<int32_t>(handleType)), "handleType", MEM_SHARED_HANDLE_TYPE_EXPECT_DESC);
constexpr uint64_t maxFlag = RT_VMM_EXPORT_FLAG_DISABLE_PID_VALIDATION;
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
(flags > maxFlag), RT_ERROR_INVALID_VALUE, "Exporting the shared handle of the AI server", flags,
"[0, " + std::to_string(maxFlag) + "]");
rtError_t error = RT_ERROR_NONE;
if (handleType == RT_MEM_SHARE_HANDLE_TYPE_FABRIC) {
Context* curCtx = Runtime::Instance()->CurrentContext();
CHECK_CONTEXT_VALID_WITH_RETURN(curCtx, RT_ERROR_CONTEXT_NULL);
NULL_PTR_RETURN_MSG(curCtx->Device_(), RT_ERROR_DEVICE_NULL);
const uint32_t devId = static_cast<uint32_t>(curCtx->Device_()->Id_());
int64_t localServerId = 0;
error = NpuDriver::GetServerId(devId, &localServerId);
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, error,
"This device does not support cross-server communication drv devId=%u localServerId=%" PRId64 " err:%#x",
devId, localServerId, static_cast<uint32_t>(error));
}
constexpr uint64_t drvFlags = 0UL;
error = NpuDriver::ExportToShareableHandleV2(handle, handleType, drvFlags, shareableHandle);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
if ((flags & RT_VMM_EXPORT_FLAG_DISABLE_PID_VALIDATION) != 0UL) {
uint64_t shareableHandleU64 = 0UL;
uint32_t serverId = 0U;
error = NpuDriver::GetServerIdAndshareableHandle(handleType, shareableHandle, &serverId, &shareableHandleU64);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
error = NpuDriver::SetMemShareHandleDisablePidVerify(shareableHandleU64);
}
RT_LOG(RT_LOG_INFO, "handleType=%s, flags=%#" PRIu64 "", MemSharedHandleTypeToString(handleType), flags);
return error;
}
rtError_t ApiImplVmm::ImportFromShareableHandle(uint64_t shareableHandle, int32_t devId, rtDrvMemHandle* handle)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
handle, RT_ERROR_INVALID_VALUE,
"Obtaining shareableHandle information and returning the handle in the current process");
int32_t realDeviceId = 0;
rtError_t error =
Runtime::Instance()->ChgUserDevIdToDeviceId(static_cast<uint32_t>(devId), RtPtrToPtr<uint32_t*>(&realDeviceId));
COND_RETURN_ERROR(
error != RT_ERROR_NONE, error, "Failed to convert the user device ID %d to driver device ID.", devId);
error = Runtime::Instance()->CheckDeviceIdIsValid(realDeviceId);
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, error, "drv devId is invalid, drv devId=%d, retCode=%#x", realDeviceId,
static_cast<uint32_t>(error));
RT_LOG(RT_LOG_INFO, "Start to ImportFromShareableHandle, drv devId=%d", realDeviceId);
COND_RETURN_ERROR(
Runtime::Instance()->CheckCurCtxValid(realDeviceId) != RT_ERROR_NONE, RT_ERROR_CONTEXT_NULL,
"Current Context is null, drv devId[%d].", realDeviceId);
uint32_t peerPhyDeviceId = 0U;
error = NpuDriver::GetPhyDevIdByMemShareHandle(shareableHandle, &peerPhyDeviceId);
if (error == RT_ERROR_DRV_NOT_SUPPORT) {
return NpuDriver::ImportFromShareableHandle(shareableHandle, realDeviceId, handle);
}
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
Context* const curCtx = Runtime::Instance()->CurrentContext(true, DEFAULT_DEVICE_ID);
CHECK_CONTEXT_VALID_WITH_RETURN(curCtx, RT_ERROR_CONTEXT_NULL);
Device* const dev = curCtx->Device_();
NULL_PTR_RETURN_MSG(dev, RT_ERROR_DEVICE_NULL);
error = dev->EnableP2PWithOtherDevice(peerPhyDeviceId);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
return NpuDriver::ImportFromShareableHandle(shareableHandle, realDeviceId, handle);
}
rtError_t ApiImplVmm::ImportFromShareableHandleV2(
const void* shareableHandle, rtMemSharedHandleType handleType, uint64_t flags, int32_t devId,
rtDrvMemHandle* handle)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
shareableHandle, RT_ERROR_INVALID_VALUE,
"Obtaining shareableHandle information and returning the handle in the current process");
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
handle, RT_ERROR_INVALID_VALUE,
"Obtaining shareableHandle information and returning the handle in the current process");
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_NAME_AND_FUNC_DESC(
(handleType != RT_MEM_SHARE_HANDLE_TYPE_DEFAULT && handleType != RT_MEM_SHARE_HANDLE_TYPE_FABRIC),
RT_ERROR_INVALID_VALUE, "Obtaining shareableHandle information and returning the handle in the current process",
RtFmtMsg("UNKNOWN(%d)", static_cast<int32_t>(handleType)), "handleType", MEM_SHARED_HANDLE_TYPE_EXPECT_DESC);
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
(flags != 0U), RT_ERROR_INVALID_VALUE,
"Obtaining shareableHandle information and returning the handle in the current process", flags, "0");
int32_t realDeviceId = 0;
rtError_t error =
Runtime::Instance()->ChgUserDevIdToDeviceId(static_cast<uint32_t>(devId), RtPtrToPtr<uint32_t*>(&realDeviceId));
COND_RETURN_ERROR(
error != RT_ERROR_NONE, error, "Failed to convert the user device ID %d to driver device ID.", devId);
error = Runtime::Instance()->CheckDeviceIdIsValid(realDeviceId);
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, error, "Device id is invalid, drv deviceId=%d, retCode=%#x", realDeviceId,
static_cast<uint32_t>(error));
UNUSED(flags);
error = RT_ERROR_NONE;
int64_t localServerId = 0;
if (handleType == RT_MEM_SHARE_HANDLE_TYPE_FABRIC) {
error = NpuDriver::GetServerId(realDeviceId, &localServerId);
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, error,
"This device does not support cross-server communication devId:%d ServerId:%" PRId64 " err:%#x",
realDeviceId, localServerId, static_cast<uint32_t>(error));
}
uint32_t peerServerId = 0U;
uint64_t shareableHandleU64 = 0UL;
error = NpuDriver::GetServerIdAndshareableHandle(handleType, shareableHandle, &peerServerId, &shareableHandleU64);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
if (handleType == RT_MEM_SHARE_HANDLE_TYPE_DEFAULT || static_cast<int64_t>(peerServerId) == localServerId) {
uint32_t peerPhyDeviceId = 0U;
error = NpuDriver::GetPhyDevIdByMemShareHandle(shareableHandleU64, &peerPhyDeviceId);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
Context* const curCtx = Runtime::Instance()->CurrentContext(true, DEFAULT_DEVICE_ID);
CHECK_CONTEXT_VALID_WITH_RETURN(curCtx, RT_ERROR_CONTEXT_NULL);
Device* const dev = curCtx->Device_();
NULL_PTR_RETURN_MSG(dev, RT_ERROR_DEVICE_NULL);
uint32_t hostId = 0U;
error = NpuDriver::GetHostID(&hostId);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
if (hostId != peerPhyDeviceId) {
error = dev->EnableP2PWithOtherDevice(peerPhyDeviceId);
}
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
}
return NpuDriver::ImportFromShareableHandleV2(shareableHandle, handleType, realDeviceId, handle);
}
rtError_t ApiImplVmm::SetPidToShareableHandle(uint64_t shareableHandle, int32_t pid[], uint32_t pidNum)
{
RT_LOG(RT_LOG_INFO, "Start to SetPidToShareableHandle");
return NpuDriver::SetPidToShareableHandle(shareableHandle, pid, pidNum);
}
rtError_t ApiImplVmm::SetPidToShareableHandleV2(
const void* shareableHandle, rtMemSharedHandleType handleType, int32_t pid[], uint32_t pidNum)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
shareableHandle, RT_ERROR_INVALID_VALUE, "Setting the trustlist of processes that can share memory");
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_NAME_AND_FUNC_DESC(
(handleType != RT_MEM_SHARE_HANDLE_TYPE_DEFAULT && handleType != RT_MEM_SHARE_HANDLE_TYPE_FABRIC),
RT_ERROR_INVALID_VALUE, "Setting the trustlist of processes that can share memory",
RtFmtMsg("UNKNOWN(%d)", static_cast<int32_t>(handleType)), "handleType", MEM_SHARED_HANDLE_TYPE_EXPECT_DESC);
rtError_t error = RT_ERROR_NONE;
uint32_t serverId = 0U;
uint64_t shareableHandleU64 = 0UL;
error = NpuDriver::GetServerIdAndshareableHandle(handleType, shareableHandle, &serverId, &shareableHandleU64);
COND_RETURN_WITH_NOLOG(error != RT_ERROR_NONE, error);
error = NpuDriver::SetPidToShareableHandle(shareableHandleU64, pid, pidNum);
RT_LOG(RT_LOG_DEBUG, "handleType = %s pidNum = %d.", MemSharedHandleTypeToString(handleType), pidNum);
return error;
}
rtError_t ApiImplVmm::GetAllocationGranularity(
rtDrvMemProp_t* prop, rtDrvMemGranularityOptions option, size_t* granularity)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
prop, RT_ERROR_INVALID_VALUE, "Querying the memory allocation granularity");
if (prop->side == DEVICE_TYPE) {
const uint32_t userDeviceId = prop->devid;
rtError_t error = Runtime::Instance()->ChgUserDevIdToDeviceId(userDeviceId, &prop->devid);
COND_RETURN_ERROR(
error != RT_ERROR_NONE, RT_ERROR_DEVICE_ID, "Failed to convert the user device ID %u to driver device ID.",
userDeviceId);
error = Runtime::Instance()->CheckDeviceIdIsValid(static_cast<int32_t>(prop->devid));
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, error, "drv devId is invalid, drv devId=%u, retCode=%#x", prop->devid,
static_cast<uint32_t>(error));
}
if (prop->side == NUMA_TYPE) {
prop->side = DRV_MEM_HOST_NUMA_SIDE;
}
RT_LOG(RT_LOG_INFO, "Start to GetAllocationGranularity");
return NpuDriver::GetAllocationGranularity(prop, option, granularity);
}
rtError_t ApiImplVmm::MemReserveAddress(
void** virPtr, size_t size, rtMallocPolicy policy, void* expectAddr, rtMallocConfig_t* cfg)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(virPtr, RT_ERROR_INVALID_VALUE, "Reserving the virtual address space");
ZERO_RETURN_AND_MSG_OUTER_WITH_FUNC_DESC(size, "Reserving the virtual address space");
COND_RETURN_AND_MSG_RESERVED_PARAM_WITH_FUNC_DESC(
(expectAddr != nullptr), RT_ERROR_INVALID_VALUE, "expectAddr",
"expectAddr is reserved parameter and must be null", "Reserving the virtual address space");
COND_RETURN_AND_MSG_RESERVED_PARAM_WITH_FUNC_DESC(
(cfg != nullptr), RT_ERROR_INVALID_VALUE, "cfg", "cfg is reserved parameter and must be null",
"Reserving the virtual address space");
UNUSED(cfg);
rtError_t error;
constexpr size_t alignment = 0U;
constexpr uint64_t flags = 1ULL;
if (static_cast<uint64_t>(policy) == static_cast<uint64_t>(RT_MEM_MALLOC_HUGE_FIRST)) {
error = NpuDriver::ReserveMemAddress(virPtr, size, alignment, expectAddr, flags);
} else if (static_cast<uint64_t>(policy) == static_cast<uint64_t>(RT_MEM_MALLOC_HUGE_ONLY)) {
error = NpuDriver::ReserveMemAddress(virPtr, size, alignment, expectAddr, flags);
} else {
RT_LOG(RT_LOG_ERROR, "flags of page type must be 0 or 1, current flags=%#llx", policy);
return RT_ERROR_INVALID_VALUE;
}
if (error != RT_ERROR_NONE) {
RT_LOG(RT_LOG_ERROR, "huge page malloc failed, error=%#x.", static_cast<int32_t>(error));
return error;
}
RT_LOG(
RT_LOG_INFO, "device malloc Succ, size=%" PRIu64 ", start ptr=0x%llx, end ptr=0x%llx", size,
RtPtrToPtr<uint64_t*>(*virPtr), RtPtrToPtr<uint64_t*>(RtPtrToPtr<uint8_t*>(*virPtr) + size));
return error;
}
rtError_t ApiImplVmm::MemMallocPhysical(rtMemHandle* handle, size_t size, rtMallocPolicy policy, rtMallocConfig_t* cfg)
{
ZERO_RETURN_AND_MSG_OUTER_WITH_FUNC_DESC(size, "Allocating the physical memory of a device");
if ((cfg != nullptr) && (cfg->attrs == nullptr)) {
RT_LOG(RT_LOG_ERROR, "cfg is not nullptr, but attrs is nullptr");
return RT_ERROR_INVALID_VALUE;
}
RT_LOG(RT_LOG_INFO, "Start to malloc physical mem.");
Context* const curCtx = Runtime::Instance()->CurrentContext(true, DEFAULT_DEVICE_ID);
CHECK_CONTEXT_VALID_WITH_RETURN(curCtx, RT_ERROR_CONTEXT_NULL);
rtDrvMemProp_t prop = {};
rtMemType_t type = RT_MEMORY_DEFAULT;
rtConfigValue_t cfgVal;
cfgVal.moduleId = static_cast<uint16_t>(APP);
cfgVal.deviceId = curCtx->Device_()->Id_();
uint32_t pgType = 0UL;
constexpr uint64_t flags = 0ULL;
if (static_cast<uint64_t>(policy) ==
static_cast<uint64_t>(RT_MEM_MALLOC_HUGE_ONLY | RT_MEM_TYPE_HIGH_BAND_WIDTH)) {
type = 0UL;
pgType = 1UL;
} else if (
static_cast<uint64_t>(policy) ==
static_cast<uint64_t>(RT_MEM_MALLOC_NORMAL_ONLY | RT_MEM_TYPE_HIGH_BAND_WIDTH)) {
type = 0UL;
pgType = 0UL;
} else {
RT_LOG(RT_LOG_ERROR, "invalid policy=%#llx, expected HBM huge-only or normal-only.", policy);
return RT_ERROR_INVALID_VALUE;
}
prop.side = 1UL;
prop.pg_type = pgType;
prop.mem_type = type;
prop.devid = cfgVal.deviceId;
prop.module_id = cfgVal.moduleId;
if (cfg == nullptr) {
return NpuDriver::MallocPhysical(RtPtrToPtr<rtDrvMemHandle*>(handle), size, &prop, flags);
}
rtError_t error = ParseMallocCfg(cfg, &cfgVal);
COND_RETURN_ERROR_MSG_INNER(
error != RT_ERROR_NONE, RT_ERROR_INVALID_VALUE, "Parse rtMallocConfig failed, error=%#x.",
static_cast<uint32_t>(error));
uint32_t realDeviceId = cfgVal.deviceId;
if (cfgVal.deviceId != curCtx->Device_()->Id_()) {
error = Runtime::Instance()->ChgUserDevIdToDeviceId(cfgVal.deviceId, &realDeviceId);
COND_RETURN_ERROR(
error != RT_ERROR_NONE, RT_ERROR_DEVICE_ID, "Failed to convert the user device ID %u to driver device ID.",
cfgVal.deviceId);
}
prop.module_id = cfgVal.moduleId > DEFAULT_MODULEID ? static_cast<uint16_t>(APP) : cfgVal.moduleId;
prop.devid = realDeviceId;
RT_LOG(
RT_LOG_INFO, "size=%" PRIu64 ", type=%#x, pgType=%#x, moduleId=%hu, deviceId=%d.", size, type, prop.pg_type,
prop.module_id, prop.devid);
return NpuDriver::MallocPhysical(RtPtrToPtr<rtDrvMemHandle*>(handle), size, &prop, flags);
}
rtError_t ApiImplVmm::MemRetainAllocationHandle(void* virPtr, rtDrvMemHandle* handle)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
virPtr, RT_ERROR_INVALID_VALUE,
"Obtaining the handle of the physical memory based on the virtual memory address");
return DoMemRetainAllocationHandle(virPtr, handle);
}
rtError_t ApiImplVmm::MemGetAllocationPropertiesFromHandle(rtDrvMemHandle handle, rtDrvMemProp_t* prop)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
handle, RT_ERROR_INVALID_VALUE,
"Querying the memory attribute information based on the handle of the physical memory information");
RT_LOG(RT_LOG_INFO, "Start to MemGetAllocationPropertiesFromHandle");
return NpuDriver::MemGetAllocationPropertiesFromHandle(handle, prop);
}
rtError_t ApiImplVmm::MemGetAddressRange(void* ptr, void** pbase, size_t* psize)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
ptr, RT_ERROR_INVALID_VALUE,
"Obtaining the start address and size of the memory block to which the address to be queried belongs");
COND_RETURN_AND_MSG_OUTER(
(pbase == nullptr) && (psize == nullptr), RT_ERROR_INVALID_VALUE, ErrorCode::EE1022,
"Obtaining the start address and size of the memory block to which the address to be queried belongs",
"nullptr and nullptr", "pbase and psize", "Parameters pbase and psize cannot both be nullptr");
return DoMemGetAddressRange(ptr, pbase, psize);
}
rtError_t ApiImplVmm::MemMapSelectedLink(void* virPtrDst, size_t size, void* virPtrSrc, uint32_t linkIdx)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
virPtrDst, RT_ERROR_INVALID_VALUE,
"Mapping the target virtual address to the physical address corresponding to the source virtual address");
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(
virPtrSrc, RT_ERROR_INVALID_VALUE,
"Mapping the target virtual address to the physical address corresponding to the source virtual address");
ZERO_RETURN_AND_MSG_OUTER_WITH_FUNC_DESC(
size, "Mapping the target virtual address to the physical address corresponding to the source virtual address");
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_AND_FUNC_DESC(
linkIdx > RT_MEM_LINK_IDX_1, RT_ERROR_INVALID_VALUE,
"Mapping the target virtual address to the physical address corresponding to the source virtual address",
linkIdx, "[" + std::to_string(RT_MEM_LINK_IDX_0) + ", " + std::to_string(RT_MEM_LINK_IDX_1) + "]");
size_t totalSize = 0U;
void* base = nullptr;
size_t baseSize = 0U;
rtDrvMemHandle handle = nullptr;
rtError_t error = RT_ERROR_NONE;
void* virPtrOld = virPtrSrc;
void* virPtrNew = virPtrDst;
while (totalSize < size) {
Runtime* rt = Runtime::Instance();
std::unique_lock<std::mutex> lock(rt->GetMemMapSelectedLinkMutex_());
error = DoMemGetAddressRange(virPtrOld, &base, &baseSize);
ERROR_RETURN(error, "Call MemGetAddressRange failed, virPtrOld=%p", virPtrOld);
COND_RETURN_ERROR(
virPtrOld != base, RT_ERROR_INVALID_VALUE,
"The address virPtrOld is not the starting address of its corresponding memory block. virPtrOld=%p, "
"base=%p",
virPtrOld, base);
error = DoMemRetainAllocationHandle(base, &handle);
ERROR_RETURN(error, "Failed to obtain the handle from virtual pointer, ptr=%p, handle=%p.", base, handle);
COND_RETURN_ERROR(
handle == nullptr, RT_ERROR_INVALID_VALUE, "virPtrSrc cannot get handle, virPtrSrc=%p", virPtrSrc);
rtHandleAttr attrOrg;
rtHandleAttr attrNew;
error = NpuDriver::MemHandleGetAttribute(handle, HANDLE_ATTR_MEM_MAP_ROUTE, &attrOrg);
COND_PROC_RETURN_ERROR(
error != RT_ERROR_NONE, error, (void)DoFreePhysical(handle),
"Call MemHandleGetAttribute failed, handle=%p, type=HANDLE_ATTR_MEM_MAP_ROUTE(%d).", handle,
HANDLE_ATTR_MEM_MAP_ROUTE);
attrNew.memMapRoute = linkIdx;
error = NpuDriver::MemHandleSetAttribute(handle, HANDLE_ATTR_MEM_MAP_ROUTE, attrNew);
COND_PROC_RETURN_ERROR(
error != RT_ERROR_NONE, error, (void)DoFreePhysical(handle),
"Call MemHandleSetAttribute failed, handle=%p, type=HANDLE_ATTR_MEM_MAP_ROUTE(%d), linkIdx=%u, "
"attrNew.memMapRoute=%u.",
handle, HANDLE_ATTR_MEM_MAP_ROUTE, linkIdx, attrNew.memMapRoute);
error = MapMem(virPtrNew, baseSize, 0, handle, 0);
COND_PROC_RETURN_ERROR(
error != RT_ERROR_NONE, error, (void)DoFreePhysical(handle),
"Call MapMem failed, baseSize=%" PRIu64 ", ptr=%p.", baseSize, virPtrNew);
error = NpuDriver::MemHandleSetAttribute(handle, HANDLE_ATTR_MEM_MAP_ROUTE, attrOrg);
COND_PROC_RETURN_ERROR(
error != RT_ERROR_NONE, error, (void)DoFreePhysical(handle),
"Call MemHandleGetAttribute failed, handle=%p, type=HANDLE_ATTR_MEM_MAP_ROUTE(%d).", handle,
HANDLE_ATTR_MEM_MAP_ROUTE);
error = DoFreePhysical(handle);
ERROR_RETURN(error, "Call FreePhysical failed, handle=%p.", handle);
virPtrOld = (uint8_t*)virPtrOld + baseSize;
virPtrNew = (uint8_t*)virPtrNew + baseSize;
totalSize += baseSize;
}
return RT_ERROR_NONE;
}
rtError_t ApiImplVmm::MemMapSetLink(rtDrvMemHandle handle, rtMemLinkType adviceLink)
{
NULL_PTR_RETURN_MSG_OUTER_WITH_FUNC_DESC(handle, RT_ERROR_INVALID_VALUE, "Setting the link ID for memory mapping");
COND_RETURN_AND_MSG_OUTER_WITH_PARAM_NAME_AND_FUNC_DESC(
adviceLink > RT_MEM_ACCESS_UB_MULTI_PORT_PATH, RT_ERROR_INVALID_VALUE, "Setting the link ID for memory mapping",
(adviceLink == RT_MEM_ACCESS_LINK_MAX) ? "MEM_ACCESS_LINK_MAX(4)" :
RtFmtMsg("UNKNOWN(%d)", static_cast<int32_t>(adviceLink)),
"adviceLink",
"[" + std::to_string(RT_MEM_ACCESS_LINK_SIO) + ", " + std::to_string(RT_MEM_ACCESS_UB_MULTI_PORT_PATH) + "]");
Runtime* rt = Runtime::Instance();
std::unique_lock<std::mutex> lock(rt->GetMemMapSelectedLinkMutex_());
rtHandleAttr attr;
attr.memMapRoute = static_cast<uint32_t>(adviceLink);
return NpuDriver::MemHandleSetAttribute(handle, HANDLE_ATTR_MEM_MAP_ROUTE, attr);
}
}
}