* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
* ubs-engine is licensed under 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 "npu_cases.h"
#include <cstring>
#include <thread>
#include <vector>
#include <gtest/gtest.h>
#include "it_assertion.h"
#include "it_console_log.h"
#include "ubs_engine_npu.h"
namespace ubse::it::tests::npu {
struct NpuDeviceWithAffinity {
ubs_ub_devices_type_t npuDev;
ubs_ub_devices_type_t affinityNicPfe;
};
static std::vector<NpuDeviceWithAffinity> ExtractNpuWithAffinityNicPfe(const ubs_ub_devices_list_t& devList)
{
std::vector<NpuDeviceWithAffinity> result;
if (devList.npu_ptr == nullptr) {
return result;
}
for (uint8_t i = 0; i < devList.npu_cnt; ++i) {
NpuDeviceWithAffinity item;
item.npuDev.device_type = UBS_NPU;
item.npuDev.slot_id = devList.npu_ptr[i].attr->slot_id;
item.npuDev.chip_id = devList.npu_ptr[i].attr->chip_id;
item.affinityNicPfe = {};
for (uint8_t j = 0; j < devList.npu_ptr[i].attr->affinity_devices_count; ++j) {
if (devList.npu_ptr[i].attr->affinity_devices[j].device_type == UBS_NIC_PFE) {
item.affinityNicPfe = devList.npu_ptr[i].attr->affinity_devices[j];
break;
}
}
result.push_back(item);
}
return result;
}
static void BuildAllocInfo(ubs_ub_alloc_devices_info_t& allocInfo, ubs_ub_devices_type_t* ubDevs, uint8_t devCount,
const uint8_t* busInstanceGuid = nullptr)
{
uint8_t upiStr[MACRO_UBSE_UB_UPI_STR_SIZE] = {'6', '4', '\0', '\0'};
memcpy(allocInfo.upi_str, upiStr, MACRO_UBSE_UB_UPI_STR_SIZE);
if (busInstanceGuid != nullptr) {
memcpy(allocInfo.bus_instance_guid, busInstanceGuid, MACRO_UBSE_UB_DEVICE_GUID_SIZE);
} else {
memset(allocInfo.bus_instance_guid, 0, MACRO_UBSE_UB_DEVICE_GUID_SIZE);
}
allocInfo.ub_dev_list = ubDevs;
allocInfo.ub_dev_list_count = devCount;
}
static void BuildFreeInfo(ubs_ub_alloc_devices_info_t& freeInfo, ubs_ub_devices_type_t* freeDevs, uint8_t devCount,
const uint8_t* busInstanceGuid)
{
uint8_t upiStr[MACRO_UBSE_UB_UPI_STR_SIZE] = {'6', '4', '\0', '\0'};
memcpy(freeInfo.upi_str, upiStr, MACRO_UBSE_UB_UPI_STR_SIZE);
memcpy(freeInfo.bus_instance_guid, busInstanceGuid, MACRO_UBSE_UB_DEVICE_GUID_SIZE);
freeInfo.ub_dev_list = freeDevs;
freeInfo.ub_dev_list_count = devCount;
}
static bool GuidIsNonZero(const uint8_t* guid)
{
for (int i = 0; i < MACRO_UBSE_UB_DEVICE_GUID_SIZE; ++i) {
if (guid[i] != 0) {
return true;
}
}
return false;
}
static bool BusiExists(const ubs_ub_devices_list_t& devList, const uint8_t* guid)
{
for (uint8_t i = 0; i < devList.busi_cnt; ++i) {
if (memcmp(devList.busi_ptr[i].attr->guid, guid, MACRO_UBSE_UB_DEVICE_GUID_SIZE) == 0) {
return true;
}
}
return false;
}
static bool NpuBoundToBusi(const ubs_ub_devices_list_t& devList, const ubs_ub_devices_type_t& npuDev,
const uint8_t* busiGuid)
{
for (uint8_t i = 0; i < devList.npu_cnt; ++i) {
if (devList.npu_ptr[i].attr->slot_id == npuDev.slot_id && devList.npu_ptr[i].attr->chip_id == npuDev.chip_id) {
return memcmp(devList.npu_ptr[i].attr->bus_instance_guid, busiGuid, MACRO_UBSE_UB_DEVICE_GUID_SIZE) == 0;
}
}
return false;
}
static bool NicPfeBoundToBusi(const ubs_ub_devices_list_t& devList, const ubs_ub_devices_type_t& nicPfeDev,
const uint8_t* busiGuid)
{
for (uint8_t i = 0; i < devList.nic_pfe_cnt; ++i) {
if (devList.nic_pfe_ptr[i].attr->slot_id == nicPfeDev.slot_id &&
devList.nic_pfe_ptr[i].attr->chip_id == nicPfeDev.chip_id &&
devList.nic_pfe_ptr[i].attr->pf_id == nicPfeDev.pf_id) {
return memcmp(devList.nic_pfe_ptr[i].attr->bus_instance_guid, busiGuid, MACRO_UBSE_UB_DEVICE_GUID_SIZE) ==
0;
}
}
return false;
}
void RunDeviceListQueryTest(ubse::it::infra::ItCluster& cluster)
{
auto& client = cluster.GetSdkClient("1");
ubs_ub_devices_list_t devList = {};
int32_t sdkRet = client.NpuDeviceListQuery(&devList);
IT_LOG_INFO << "NpuDeviceListQuery returned: " << sdkRet;
EXPECT_EQ(sdkRet, UBS_SUCCESS);
if (sdkRet == UBS_SUCCESS) {
auto npuItems = ExtractNpuWithAffinityNicPfe(devList);
IT_LOG_INFO << "NPU count: " << devList.npu_cnt << ", BUSI count: " << devList.busi_cnt
<< ", NIC_PFE count: " << devList.nic_pfe_cnt;
ASSERT_GE(npuItems.size(), 1u) << "Should have at least one NPU device";
EXPECT_EQ(devList.busi_cnt, 0u) << "No VM bus instance before alloc";
EXPECT_GE(devList.nic_pfe_cnt, 1u) << "Should have at least one NIC_PFE device";
for (size_t i = 0; i < npuItems.size(); ++i) {
IT_LOG_INFO << "NPU[" << i << "]: slot=" << npuItems[i].npuDev.slot_id
<< ", chip=" << npuItems[i].npuDev.chip_id
<< ", affinity NIC: slot=" << npuItems[i].affinityNicPfe.slot_id
<< ", chip=" << npuItems[i].affinityNicPfe.chip_id
<< ", pf=" << npuItems[i].affinityNicPfe.pf_id;
}
}
client.NpuDeviceListFree(&devList);
}
void RunDeviceAllocFreeLifecycleTest(ubse::it::infra::ItCluster& cluster)
{
auto& client = cluster.GetSdkClient("1");
ubs_ub_devices_list_t queryList = {};
int32_t sdkRet = client.NpuDeviceListQuery(&queryList);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
auto npuItems = ExtractNpuWithAffinityNicPfe(queryList);
client.NpuDeviceListFree(&queryList);
ASSERT_GE(npuItems.size(), 1u);
ubs_ub_alloc_devices_info_t allocInfo = {};
ubs_ub_devices_type_t ubDevs[2] = {};
ubDevs[0] = npuItems[0].npuDev;
ubDevs[1] = npuItems[0].affinityNicPfe;
BuildAllocInfo(allocInfo, ubDevs, 2);
uint8_t newBusInstanceGuid[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
ubs_ub_devices_list_t allocDevList = {};
IT_LOG_INFO << "Allocating NPU+NIC_PFE devices";
sdkRet = client.NpuDeviceAlloc(&allocInfo, newBusInstanceGuid, &allocDevList);
IT_LOG_INFO << "NpuDeviceAlloc returned: " << sdkRet;
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "NPU alloc should succeed";
if (sdkRet == UBS_SUCCESS) {
EXPECT_GT(allocDevList.npu_cnt, 0u);
EXPECT_GT(allocDevList.busi_cnt, 0u);
EXPECT_TRUE(GuidIsNonZero(newBusInstanceGuid)) << "newBusInstanceGuid should not be all zeros";
}
ubs_ub_alloc_devices_info_t freeInfo = {};
ubs_ub_devices_type_t freeDevs[2] = {};
freeDevs[0] = npuItems[0].npuDev;
freeDevs[1] = npuItems[0].affinityNicPfe;
BuildFreeInfo(freeInfo, freeDevs, 2, newBusInstanceGuid);
IT_LOG_INFO << "Freeing NPU device";
sdkRet = client.NpuDeviceFree(&freeInfo);
IT_LOG_INFO << "NpuDeviceFree returned: " << sdkRet;
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "NPU free should succeed";
client.NpuDeviceListFree(&allocDevList);
}
void RunUbaTidSizeQueryTest(ubse::it::infra::ItCluster& cluster)
{
auto& client = cluster.GetSdkClient("1");
ubs_ub_devices_list_t queryList = {};
int32_t sdkRet = client.NpuDeviceListQuery(&queryList);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
auto npuItems = ExtractNpuWithAffinityNicPfe(queryList);
client.NpuDeviceListFree(&queryList);
ASSERT_GE(npuItems.size(), 1u) << "Need at least one NPU device for UbaTidSize query";
ubs_ub_alloc_devices_info_t allocInfo = {};
ubs_ub_devices_type_t ubDevs[2] = {};
ubDevs[0] = npuItems[0].npuDev;
ubDevs[1] = npuItems[0].affinityNicPfe;
BuildAllocInfo(allocInfo, ubDevs, 2);
uint8_t newBusInstanceGuid[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
ubs_ub_devices_list_t allocDevList = {};
sdkRet = client.NpuDeviceAlloc(&allocInfo, newBusInstanceGuid, &allocDevList);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "Alloc should succeed before UbaTidSize query";
if (sdkRet == UBS_SUCCESS) {
uint32_t tid = 0;
uint64_t uba = 0;
uint64_t size = 0;
IT_LOG_INFO << "Querying UBA/TID/Size with allocated bus instance GUID";
sdkRet = client.UbaTidSizeQuery(newBusInstanceGuid, &tid, &uba, &size);
IT_LOG_INFO << "UbaTidSizeQuery returned: " << sdkRet << ", tid=" << tid << ", uba=" << uba
<< ", size=" << size;
EXPECT_EQ(sdkRet, UBS_SUCCESS);
EXPECT_GT(tid, 0u);
EXPECT_GT(uba, 0ull);
EXPECT_GT(size, 0ull);
}
ubs_ub_alloc_devices_info_t freeInfo = {};
ubs_ub_devices_type_t freeDevs[2] = {};
freeDevs[0] = npuItems[0].npuDev;
freeDevs[1] = npuItems[0].affinityNicPfe;
BuildFreeInfo(freeInfo, freeDevs, 2, newBusInstanceGuid);
sdkRet = client.NpuDeviceFree(&freeInfo);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "NPU free should succeed after UbaTidSize query";
client.NpuDeviceListFree(&allocDevList);
}
* 用例名称:验证重复调用使能接口成功RepeatAllocAndFree
* 用例预置条件:P1: ubse已就绪
* 用例测试步骤:
* S1. 调用查询SDK,查询环境上可用npu和1825设备
* S2. 随机选择S1中的npu和1825设备,连续调用使能SDK(两次使用的设备不同),检查是否使能成功
* S3. 调用查询SDK,记录此结果
* S4. 随机选择一个S2中创建的bus instance对应的guid,再次随机选择设备,调用使能SDK,检查是否使能成功
* S5. 调用查询接口,检查S4中覆盖的bus instance是否覆盖成功
* 用例预期结果:
* E2. 均使能成功
* E4. 使能成功
* E5. 覆盖成功
* 用例后置清理:调用去使能接口,销毁S2创建的bus instance
*/
void RunRepeatAllocAndFreeTest(ubse::it::infra::ItCluster& cluster)
{
auto& client = cluster.GetSdkClient("1");
ubs_ub_devices_list_t queryList = {};
int32_t sdkRet = client.NpuDeviceListQuery(&queryList);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
auto npuItems = ExtractNpuWithAffinityNicPfe(queryList);
client.NpuDeviceListFree(&queryList);
ASSERT_GE(npuItems.size(), 3u) << "Need at least 3 NPU devices for repeat alloc";
for (size_t i = 0; i < 3; ++i) {
IT_LOG_INFO << "NPU[" << i << "]: chip=" << npuItems[i].npuDev.chip_id
<< ", affinity NIC: chip=" << npuItems[i].affinityNicPfe.chip_id
<< ", pf=" << npuItems[i].affinityNicPfe.pf_id;
}
uint8_t guid1[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
uint8_t guid2[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
ubs_ub_devices_list_t devList1 = {};
ubs_ub_devices_list_t devList2 = {};
ubs_ub_alloc_devices_info_t allocInfo1 = {};
ubs_ub_devices_type_t ubDevs1[2] = {};
ubDevs1[0] = npuItems[0].npuDev;
ubDevs1[1] = npuItems[0].affinityNicPfe;
BuildAllocInfo(allocInfo1, ubDevs1, 2);
IT_LOG_INFO << "S2: First alloc (NPU chip=" << npuItems[0].npuDev.chip_id << " + affinity NIC)";
sdkRet = client.NpuDeviceAlloc(&allocInfo1, guid1, &devList1);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "E2: First alloc should succeed";
EXPECT_GT(devList1.busi_cnt, 0u);
ubs_ub_alloc_devices_info_t allocInfo2 = {};
ubs_ub_devices_type_t ubDevs2[2] = {};
ubDevs2[0] = npuItems[1].npuDev;
ubDevs2[1] = npuItems[1].affinityNicPfe;
BuildAllocInfo(allocInfo2, ubDevs2, 2);
IT_LOG_INFO << "S2: Second alloc (NPU chip=" << npuItems[1].npuDev.chip_id << " + affinity NIC)";
sdkRet = client.NpuDeviceAlloc(&allocInfo2, guid2, &devList2);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "E2: Second alloc should succeed";
EXPECT_GT(devList2.busi_cnt, 0u);
EXPECT_NE(memcmp(guid1, guid2, MACRO_UBSE_UB_DEVICE_GUID_SIZE), 0)
<< "Two allocs should return different bus instance GUIDs";
ubs_ub_devices_list_t queryListAfterS2 = {};
sdkRet = client.NpuDeviceListQuery(&queryListAfterS2);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
EXPECT_GE(queryListAfterS2.busi_cnt, 2u) << "S3: Should have at least 2 BUSI after two allocs";
EXPECT_TRUE(NpuBoundToBusi(queryListAfterS2, npuItems[0].npuDev, guid1))
<< "S3: npuItems[0] should be bound to guid1";
EXPECT_TRUE(NicPfeBoundToBusi(queryListAfterS2, npuItems[0].affinityNicPfe, guid1))
<< "S3: npuItems[0] affinity NIC should be bound to guid1";
EXPECT_TRUE(NpuBoundToBusi(queryListAfterS2, npuItems[1].npuDev, guid2))
<< "S3: npuItems[1] should be bound to guid2";
EXPECT_TRUE(NicPfeBoundToBusi(queryListAfterS2, npuItems[1].affinityNicPfe, guid2))
<< "S3: npuItems[1] affinity NIC should be bound to guid2";
client.NpuDeviceListFree(&queryListAfterS2);
uint8_t reusedGuid[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
memcpy(reusedGuid, guid1, MACRO_UBSE_UB_DEVICE_GUID_SIZE);
ubs_ub_alloc_devices_info_t allocInfo3 = {};
ubs_ub_devices_type_t ubDevs3[2] = {};
ubDevs3[0] = npuItems[2].npuDev;
ubDevs3[1] = npuItems[2].affinityNicPfe;
BuildAllocInfo(allocInfo3, ubDevs3, 2, reusedGuid);
ubs_ub_devices_list_t devList3 = {};
IT_LOG_INFO << "S4: Alloc with existing bus instance guid1, using NPU chip=" << npuItems[2].npuDev.chip_id
<< " + affinity NIC";
sdkRet = client.NpuDeviceAlloc(&allocInfo3, reusedGuid, &devList3);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "E4: Alloc with existing guid should succeed";
bool guidMatch = (memcmp(reusedGuid, guid1, MACRO_UBSE_UB_DEVICE_GUID_SIZE) == 0);
EXPECT_TRUE(guidMatch) << "E4: Returned guid should match the input guid1";
ubs_ub_devices_list_t queryListAfterS4 = {};
sdkRet = client.NpuDeviceListQuery(&queryListAfterS4);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
EXPECT_GE(queryListAfterS4.busi_cnt, 2u) << "E5: BUSI should exist after preempt alloc";
EXPECT_TRUE(NpuBoundToBusi(queryListAfterS4, npuItems[2].npuDev, reusedGuid))
<< "E5: npuItems[2] should be bound to reusedGuid bus instance";
EXPECT_TRUE(NicPfeBoundToBusi(queryListAfterS4, npuItems[2].affinityNicPfe, reusedGuid))
<< "E5: npuItems[2] affinity NIC should be bound to reusedGuid";
EXPECT_FALSE(NpuBoundToBusi(queryListAfterS4, npuItems[0].npuDev, reusedGuid))
<< "E5: npuItems[0] should NOT be bound to reusedGuid after override";
EXPECT_FALSE(NicPfeBoundToBusi(queryListAfterS4, npuItems[0].affinityNicPfe, reusedGuid))
<< "E5: npuItems[0] affinity NIC should NOT be bound to reusedGuid after override";
client.NpuDeviceListFree(&queryListAfterS4);
ubs_ub_alloc_devices_info_t freeInfo1 = {};
ubs_ub_devices_type_t freeDevs1[2] = {};
freeDevs1[0] = npuItems[2].npuDev;
freeDevs1[1] = npuItems[2].affinityNicPfe;
BuildFreeInfo(freeInfo1, freeDevs1, 2, guid1);
client.NpuDeviceFree(&freeInfo1);
ubs_ub_alloc_devices_info_t freeInfo2 = {};
ubs_ub_devices_type_t freeDevs2[2] = {};
freeDevs2[0] = npuItems[1].npuDev;
freeDevs2[1] = npuItems[1].affinityNicPfe;
BuildFreeInfo(freeInfo2, freeDevs2, 2, guid2);
client.NpuDeviceFree(&freeInfo2);
client.NpuDeviceListFree(&devList1);
client.NpuDeviceListFree(&devList2);
client.NpuDeviceListFree(&devList3);
}
* 用例名称:验证调用使能接口抢占设备成功
* 用例预置条件:
* P1: ubse已就绪
* P2: 已经创建bus instance(绑定2组NPU+亲和NIC_PFE共4设备)
* 用例测试步骤:
* S1. 传入设备列表为部分P2中bus instance绑定的设备(第1组NPU+亲和NIC),调用使能SDK,检查使能是否成功
* S2. 调用查询接口,检查S1中创建的bus instance是否存在,绑定的设备是否正确
* S3. 检查P2中的bus instance所绑定的设备是否被抢占(仅剩未在S1中传入的设备)
* S4. 传入S1中剩余未传入的设备(第2组NPU+亲和NIC),调用使能接口,检查是否使能成功
* S5. 调用查询接口,检查P2中的bus instance是否被删除,S1/S4中创建的bus instance所绑定的设备是否正确
* 用例预期结果:
* E1. 使能成功
* E2. 存在,设备正确
* E3. 设备被抢占
* E4. 使能成功
* E5. 被删除,设备正确
* 用例后置清理:调用去使能接口,销毁S1/S4创建的bus instance
*/
void RunPreemptDeviceTest(ubse::it::infra::ItCluster& cluster)
{
auto& client = cluster.GetSdkClient("1");
ubs_ub_devices_list_t queryList = {};
int32_t sdkRet = client.NpuDeviceListQuery(&queryList);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
auto npuItems = ExtractNpuWithAffinityNicPfe(queryList);
client.NpuDeviceListFree(&queryList);
ASSERT_GE(npuItems.size(), 2u) << "Need at least 2 NPU devices for preempt test";
uint8_t guidP2[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
ubs_ub_alloc_devices_info_t allocInfoP2 = {};
ubs_ub_devices_type_t ubDevsP2[4] = {};
ubDevsP2[0] = npuItems[0].npuDev;
ubDevsP2[1] = npuItems[0].affinityNicPfe;
ubDevsP2[2] = npuItems[1].npuDev;
ubDevsP2[3] = npuItems[1].affinityNicPfe;
BuildAllocInfo(allocInfoP2, ubDevsP2, 4);
ubs_ub_devices_list_t devListP2 = {};
IT_LOG_INFO << "P2: Alloc 2 NPU+NIC pairs into one bus instance";
sdkRet = client.NpuDeviceAlloc(&allocInfoP2, guidP2, &devListP2);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "P2: Alloc should succeed";
uint8_t guidS1[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
ubs_ub_alloc_devices_info_t allocInfoS1 = {};
ubs_ub_devices_type_t ubDevsS1[2] = {};
ubDevsS1[0] = npuItems[0].npuDev;
ubDevsS1[1] = npuItems[0].affinityNicPfe;
BuildAllocInfo(allocInfoS1, ubDevsS1, 2);
ubs_ub_devices_list_t devListS1 = {};
IT_LOG_INFO << "S1: Alloc partial devices from P2 bus instance";
sdkRet = client.NpuDeviceAlloc(&allocInfoS1, guidS1, &devListS1);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "E1: Alloc should succeed";
ubs_ub_devices_list_t queryAfterS1 = {};
sdkRet = client.NpuDeviceListQuery(&queryAfterS1);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
EXPECT_TRUE(BusiExists(queryAfterS1, guidS1)) << "E2: S1 bus instance should exist";
EXPECT_TRUE(NpuBoundToBusi(queryAfterS1, npuItems[0].npuDev, guidS1))
<< "E2: npuDev0 should be bound to S1 bus instance";
EXPECT_TRUE(NicPfeBoundToBusi(queryAfterS1, npuItems[0].affinityNicPfe, guidS1))
<< "E2: nicPfe0 should be bound to S1 bus instance";
EXPECT_FALSE(NpuBoundToBusi(queryAfterS1, npuItems[0].npuDev, guidP2)) << "E3: npuDev0 should be preempted from P2";
EXPECT_FALSE(NicPfeBoundToBusi(queryAfterS1, npuItems[0].affinityNicPfe, guidP2))
<< "E3: nicPfe0 should be preempted from P2";
EXPECT_TRUE(NpuBoundToBusi(queryAfterS1, npuItems[1].npuDev, guidP2)) << "E3: npuDev1 should remain in P2";
EXPECT_TRUE(NicPfeBoundToBusi(queryAfterS1, npuItems[1].affinityNicPfe, guidP2))
<< "E3: nicPfe1 should remain in P2";
client.NpuDeviceListFree(&queryAfterS1);
uint8_t guidS4[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
ubs_ub_alloc_devices_info_t allocInfoS4 = {};
ubs_ub_devices_type_t ubDevsS4[2] = {};
ubDevsS4[0] = npuItems[1].npuDev;
ubDevsS4[1] = npuItems[1].affinityNicPfe;
BuildAllocInfo(allocInfoS4, ubDevsS4, 2);
ubs_ub_devices_list_t devListS4 = {};
IT_LOG_INFO << "S4: Alloc remaining devices from P2 bus instance";
sdkRet = client.NpuDeviceAlloc(&allocInfoS4, guidS4, &devListS4);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "E4: Alloc should succeed";
ubs_ub_devices_list_t queryAfterS5 = {};
sdkRet = client.NpuDeviceListQuery(&queryAfterS5);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
EXPECT_FALSE(BusiExists(queryAfterS5, guidP2)) << "E5: P2 bus instance should be deleted";
EXPECT_TRUE(BusiExists(queryAfterS5, guidS1)) << "E5: S1 bus instance should exist";
EXPECT_TRUE(NpuBoundToBusi(queryAfterS5, npuItems[0].npuDev, guidS1))
<< "E5: npuDev0 should be bound to S1 bus instance";
EXPECT_TRUE(NicPfeBoundToBusi(queryAfterS5, npuItems[0].affinityNicPfe, guidS1))
<< "E5: nicPfe0 should be bound to S1 bus instance";
EXPECT_TRUE(NpuBoundToBusi(queryAfterS5, npuItems[1].npuDev, guidS4))
<< "E5: npuDev1 should be bound to S4 bus instance";
EXPECT_TRUE(NicPfeBoundToBusi(queryAfterS5, npuItems[1].affinityNicPfe, guidS4))
<< "E5: nicPfe1 should be bound to S4 bus instance";
client.NpuDeviceListFree(&queryAfterS5);
ubs_ub_alloc_devices_info_t freeInfoS1 = {};
ubs_ub_devices_type_t freeDevsS1[2] = {};
freeDevsS1[0] = npuItems[0].npuDev;
freeDevsS1[1] = npuItems[0].affinityNicPfe;
BuildFreeInfo(freeInfoS1, freeDevsS1, 2, guidS1);
client.NpuDeviceFree(&freeInfoS1);
ubs_ub_alloc_devices_info_t freeInfoS4 = {};
ubs_ub_devices_type_t freeDevsS4[2] = {};
freeDevsS4[0] = npuItems[1].npuDev;
freeDevsS4[1] = npuItems[1].affinityNicPfe;
BuildFreeInfo(freeInfoS4, freeDevsS4, 2, guidS4);
client.NpuDeviceFree(&freeInfoS4);
client.NpuDeviceListFree(&devListP2);
client.NpuDeviceListFree(&devListS1);
client.NpuDeviceListFree(&devListS4);
}
* 用例名称:验证重复调用去使能接口
* 用例预置条件:
* P1: ubse已就绪
* P2: 已经创建bus instance
* 用例测试步骤:
* S1. 传入P2中bus instance对应的guid,调用去使能接口,检查去使能接口是否成功
* S2. 重复S1,检查是否成功
* 用例预期结果:
* E1. 去使能成功
* E2. 去使能失败
* 用例后置清理:无
*/
void RunRepeatDeallocTest(ubse::it::infra::ItCluster& cluster)
{
auto& client = cluster.GetSdkClient("1");
ubs_ub_devices_list_t queryList = {};
int32_t sdkRet = client.NpuDeviceListQuery(&queryList);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
auto npuItems = ExtractNpuWithAffinityNicPfe(queryList);
client.NpuDeviceListFree(&queryList);
ASSERT_GE(npuItems.size(), 1u);
uint8_t guid[MACRO_UBSE_UB_DEVICE_GUID_SIZE] = {};
ubs_ub_alloc_devices_info_t allocInfo = {};
ubs_ub_devices_type_t ubDevs[2] = {};
ubDevs[0] = npuItems[0].npuDev;
ubDevs[1] = npuItems[0].affinityNicPfe;
BuildAllocInfo(allocInfo, ubDevs, 2);
ubs_ub_devices_list_t allocDevList = {};
sdkRet = client.NpuDeviceAlloc(&allocInfo, guid, &allocDevList);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "P2: Alloc should succeed";
ubs_ub_alloc_devices_info_t freeInfo1 = {};
ubs_ub_devices_type_t freeDevs1[2] = {};
freeDevs1[0] = npuItems[0].npuDev;
freeDevs1[1] = npuItems[0].affinityNicPfe;
BuildFreeInfo(freeInfo1, freeDevs1, 2, guid);
IT_LOG_INFO << "S1: First free call";
sdkRet = client.NpuDeviceFree(&freeInfo1);
EXPECT_EQ(sdkRet, UBS_SUCCESS) << "E1: First free should succeed";
ubs_ub_devices_list_t queryAfterFree = {};
sdkRet = client.NpuDeviceListQuery(&queryAfterFree);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
EXPECT_FALSE(BusiExists(queryAfterFree, guid)) << "E1: Bus instance should not exist after free";
EXPECT_FALSE(NpuBoundToBusi(queryAfterFree, npuItems[0].npuDev, guid))
<< "E1: NPU bus_instance_guid should not match freed guid";
client.NpuDeviceListFree(&queryAfterFree);
ubs_ub_alloc_devices_info_t freeInfo2 = {};
ubs_ub_devices_type_t freeDevs2[2] = {};
freeDevs2[0] = npuItems[0].npuDev;
freeDevs2[1] = npuItems[0].affinityNicPfe;
BuildFreeInfo(freeInfo2, freeDevs2, 2, guid);
IT_LOG_INFO << "S2: Second free call (should fail)";
sdkRet = client.NpuDeviceFree(&freeInfo2);
EXPECT_NE(sdkRet, UBS_SUCCESS) << "E2: Second free should fail";
client.NpuDeviceListFree(&allocDevList);
}
* 用例名称:验证接口并发成功
* 用例预置条件:P1: ubse已就绪
* 用例测试步骤:
* S1. 并发调用查询接口,检查调用是否成功
* S2. 并发调用使能接口,检查调用是否成功
* S3. 并发调用去使能接口,检查调用是否成功
* 用例预期结果:
* E1. 查询成功
* E2. 使能成功
* E3. 去使能成功
* 用例后置清理:无
*/
void RunConcurrentSuccessTest(ubse::it::infra::ItCluster& cluster)
{
auto& client = cluster.GetSdkClient("1");
const int THREAD_COUNT = 4;
ubs_ub_devices_list_t queryList = {};
int32_t sdkRet = client.NpuDeviceListQuery(&queryList);
EXPECT_EQ(sdkRet, UBS_SUCCESS);
auto npuItems = ExtractNpuWithAffinityNicPfe(queryList);
client.NpuDeviceListFree(&queryList);
ASSERT_GE(npuItems.size(), (size_t)THREAD_COUNT) << "Need enough NPU devices for concurrent test";
std::vector<int32_t> queryResults(THREAD_COUNT, -1);
std::vector<std::thread> threads;
for (int i = 0; i < THREAD_COUNT; ++i) {
threads.emplace_back([&client, &queryResults, i]() {
ubs_ub_devices_list_t devList = {};
queryResults[i] = client.NpuDeviceListQuery(&devList);
client.NpuDeviceListFree(&devList);
});
}
for (auto& t : threads) {
t.join();
}
for (int i = 0; i < THREAD_COUNT; ++i) {
EXPECT_EQ(queryResults[i], UBS_SUCCESS) << "E1: Concurrent query " << i << " should succeed";
}
std::vector<uint8_t> guids(THREAD_COUNT * MACRO_UBSE_UB_DEVICE_GUID_SIZE, 0);
std::vector<int32_t> allocResults(THREAD_COUNT, -1);
threads.clear();
for (int i = 0; i < THREAD_COUNT; ++i) {
threads.emplace_back([&client, &npuItems, &guids, &allocResults, i]() {
ubs_ub_alloc_devices_info_t allocInfo = {};
ubs_ub_devices_type_t ubDevs[2] = {};
ubDevs[0] = npuItems[i].npuDev;
ubDevs[1] = npuItems[i].affinityNicPfe;
BuildAllocInfo(allocInfo, ubDevs, 2);
ubs_ub_devices_list_t allocDevList = {};
allocResults[i] =
client.NpuDeviceAlloc(&allocInfo, &guids[i * MACRO_UBSE_UB_DEVICE_GUID_SIZE], &allocDevList);
client.NpuDeviceListFree(&allocDevList);
});
}
for (auto& t : threads) {
t.join();
}
for (int i = 0; i < THREAD_COUNT; ++i) {
EXPECT_EQ(allocResults[i], UBS_SUCCESS) << "E2: Concurrent alloc " << i << " should succeed";
}
for (int i = 0; i < THREAD_COUNT; ++i) {
EXPECT_TRUE(GuidIsNonZero(&guids[i * MACRO_UBSE_UB_DEVICE_GUID_SIZE]))
<< "E2: Alloc " << i << " should return non-zero bus instance guid";
}
for (int i = 1; i < THREAD_COUNT; ++i) {
EXPECT_NE(memcmp(&guids[0], &guids[i * MACRO_UBSE_UB_DEVICE_GUID_SIZE], MACRO_UBSE_UB_DEVICE_GUID_SIZE), 0)
<< "E2: Alloc " << i << " should return different guid from alloc 0";
}
std::vector<int32_t> freeResults(THREAD_COUNT, -1);
threads.clear();
for (int i = 0; i < THREAD_COUNT; ++i) {
threads.emplace_back([&client, &npuItems, &guids, &freeResults, i]() {
ubs_ub_alloc_devices_info_t freeInfo = {};
ubs_ub_devices_type_t freeDevs[2] = {};
freeDevs[0] = npuItems[i].npuDev;
freeDevs[1] = npuItems[i].affinityNicPfe;
BuildFreeInfo(freeInfo, freeDevs, 2, &guids[i * MACRO_UBSE_UB_DEVICE_GUID_SIZE]);
freeResults[i] = client.NpuDeviceFree(&freeInfo);
});
}
for (auto& t : threads) {
t.join();
}
for (int i = 0; i < THREAD_COUNT; ++i) {
EXPECT_EQ(freeResults[i], UBS_SUCCESS) << "E3: Concurrent free " << i << " should succeed";
}
}
}