CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
307 lines
11 KiB
C++
307 lines
11 KiB
C++
/*************************************************************************
|
|
* Copyright (c) 2026 Advanced Micro Devices, Inc. All rights reserved.
|
|
*
|
|
* See LICENSE.txt for license information
|
|
************************************************************************/
|
|
|
|
#include "amdsmi_wrap.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <gtest/gtest.h>
|
|
|
|
namespace RcclUnitTesting
|
|
{
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Fabric info version acceptance
|
|
//
|
|
// amdsmi_get_gpu_fabric_info() returns SUCCESS with a fully populated payload
|
|
// but never writes the version field, leaving it at the sentinel amd_smi uses
|
|
// for anything it could not source from sysfs. Rejecting that sentinel made
|
|
// RCCL_USE_AMD_SMI_LIB=1 discard working UALoE fabric on every device.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
TEST(AmdSmiFabricVersion, AcceptsUnreportedSentinel)
|
|
{
|
|
EXPECT_TRUE(amdSmiFabricVersionUsable(kAmdSmiFabricVersionUnreported));
|
|
}
|
|
|
|
TEST(AmdSmiFabricVersion, AcceptsCurrentVersion)
|
|
{
|
|
EXPECT_TRUE(amdSmiFabricVersionUsable(AMDSMI_FABRIC_INFO_CURRENT_VERSION));
|
|
}
|
|
|
|
// A version the library actually claims, but whose union layout we do not
|
|
// implement, must still be refused rather than misparsed as v1.
|
|
TEST(AmdSmiFabricVersion, RejectsUnimplementedFutureVersion)
|
|
{
|
|
EXPECT_FALSE(amdSmiFabricVersionUsable(AMDSMI_FABRIC_INFO_CURRENT_VERSION + 1));
|
|
}
|
|
|
|
// Distinct from the sentinel: zero means the library returned without writing
|
|
// the field at all, which contradicts a SUCCESS status and is worth surfacing.
|
|
TEST(AmdSmiFabricVersion, RejectsZero)
|
|
{
|
|
EXPECT_FALSE(amdSmiFabricVersionUsable(0));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Fabric type / accelerator state eligibility
|
|
//
|
|
// This is the decision the version check was gating: only a link type we drive
|
|
// combined with a state whose vPoD can carry traffic counts as usable.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
TEST(AmdSmiFabricState, UaloeReadyIsUsable)
|
|
{
|
|
EXPECT_TRUE(amdSmiFabricStateUsable(AMDSMI_FABRIC_TYPE_UALOE,
|
|
AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_READY));
|
|
}
|
|
|
|
TEST(AmdSmiFabricState, UaloeActiveIsUsable)
|
|
{
|
|
EXPECT_TRUE(amdSmiFabricStateUsable(AMDSMI_FABRIC_TYPE_UALOE,
|
|
AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_ACTIVE));
|
|
}
|
|
|
|
TEST(AmdSmiFabricState, UalinkReadyIsUsable)
|
|
{
|
|
EXPECT_TRUE(amdSmiFabricStateUsable(AMDSMI_FABRIC_TYPE_UALLINK,
|
|
AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_READY));
|
|
}
|
|
|
|
TEST(AmdSmiFabricState, ConfiguredButNotReadyIsNotUsable)
|
|
{
|
|
EXPECT_FALSE(amdSmiFabricStateUsable(AMDSMI_FABRIC_TYPE_UALOE,
|
|
AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_CONFIGURED));
|
|
}
|
|
|
|
TEST(AmdSmiFabricState, UnconfiguredIsNotUsable)
|
|
{
|
|
EXPECT_FALSE(amdSmiFabricStateUsable(AMDSMI_FABRIC_TYPE_UALOE,
|
|
AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_UNCONFIGURED));
|
|
}
|
|
|
|
TEST(AmdSmiFabricState, ErrorStateIsNotUsable)
|
|
{
|
|
EXPECT_FALSE(amdSmiFabricStateUsable(AMDSMI_FABRIC_TYPE_UALOE,
|
|
AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_ERROR));
|
|
}
|
|
|
|
TEST(AmdSmiFabricState, UnknownLinkTypeIsNotUsable)
|
|
{
|
|
EXPECT_FALSE(amdSmiFabricStateUsable(AMDSMI_FABRIC_TYPE_UNKNOWN,
|
|
AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_ACTIVE));
|
|
}
|
|
|
|
// The combination this node actually reports, and that the version check used
|
|
// to throw away: UALoE + READY reached through the full struct.
|
|
TEST(AmdSmiFabricState, PayloadFromSysfsBackedDeviceIsUsable)
|
|
{
|
|
amdsmi_fabric_info_v1_t v1{};
|
|
v1.fabric_type = AMDSMI_FABRIC_TYPE_UALOE;
|
|
v1.accel_state = AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_READY;
|
|
v1.accelerator_id = 3;
|
|
v1.ppod_size = 8;
|
|
v1.vpod_id = 1;
|
|
v1.vpod_size = 4;
|
|
|
|
EXPECT_TRUE(amdSmiFabricVersionUsable(kAmdSmiFabricVersionUnreported));
|
|
EXPECT_TRUE(amdSmiFabricStateUsable(v1.fabric_type, v1.accel_state));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Struct layout
|
|
//
|
|
// The library writes these structs through dlopen using its own layout, so a
|
|
// declaration that disagrees overflows the caller's object and shifts the
|
|
// fields we read. amdsmi_wrap.h accepts the coherent 8-GPU layout used by some
|
|
// pre-release ROCm 7.14 snapshots and the 16-GPU layout used by final ROCm 7.14
|
|
// and ROCm 7.15, but rejects any other combination.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
TEST(AmdSmiFabricLayout, MatchesShippedLibraryAbi)
|
|
{
|
|
EXPECT_TRUE(amdSmiFabricLayoutIs8Gpu || amdSmiFabricLayoutIs16Gpu);
|
|
EXPECT_NE(amdSmiFabricLayoutIs8Gpu, amdSmiFabricLayoutIs16Gpu);
|
|
}
|
|
|
|
TEST(AmdSmiFabricLayout, TrailingFieldsAreNotShifted)
|
|
{
|
|
const size_t expectedAddrMode = amdSmiFabricLayoutIs8Gpu ? 204u : 236u;
|
|
EXPECT_EQ(offsetof(amdsmi_fabric_info_v1_t, addr_mode), expectedAddrMode);
|
|
EXPECT_EQ(offsetof(amdsmi_fabric_info_v1_t, accel_state), expectedAddrMode + sizeof(uint32_t));
|
|
}
|
|
|
|
TEST(AmdSmiFabricRuntimeLayout, DetectsEightGpuWriter)
|
|
{
|
|
amdSmiFabricInfoBuffer buffer;
|
|
amdSmiPrepareFabricInfoBuffer(buffer);
|
|
memset(buffer.bytes, 0, kAmdSmiFabricInfo8GpuSize);
|
|
|
|
EXPECT_EQ(amdSmiDetectFabricRuntimeLayout(buffer), amdSmiFabricRuntimeLayout::EightGpu);
|
|
}
|
|
|
|
TEST(AmdSmiFabricRuntimeLayout, DetectsSixteenGpuWriter)
|
|
{
|
|
amdSmiFabricInfoBuffer buffer;
|
|
amdSmiPrepareFabricInfoBuffer(buffer);
|
|
memset(buffer.bytes, 0, kAmdSmiFabricInfo16GpuSize);
|
|
|
|
EXPECT_EQ(amdSmiDetectFabricRuntimeLayout(buffer), amdSmiFabricRuntimeLayout::SixteenGpu);
|
|
}
|
|
|
|
TEST(AmdSmiFabricRuntimeLayout, RejectsUnknownWriter)
|
|
{
|
|
amdSmiFabricInfoBuffer buffer;
|
|
amdSmiPrepareFabricInfoBuffer(buffer);
|
|
buffer.bytes[kAmdSmiFabricInfo8GpuSize] = 0;
|
|
|
|
EXPECT_EQ(amdSmiDetectFabricRuntimeLayout(buffer), amdSmiFabricRuntimeLayout::Unknown);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Both amdsmi_fabric_info_t shapes
|
|
//
|
|
// amd_smi 27.0 flattened the struct, so RCCL has to read a version and a v1
|
|
// payload out of either shape. Only one of them exists in any given build, so
|
|
// stand both up as mocks here: that is the only way to compile and exercise the
|
|
// branch the installed header does not select.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
namespace
|
|
{
|
|
union MockFabricPayload
|
|
{
|
|
amdsmi_fabric_info_v1_t v1;
|
|
};
|
|
|
|
// Pre-27: version and payload sit inside fabric_info, where the union carries
|
|
// the fabric_version name.
|
|
struct MockNestedFabricVer
|
|
{
|
|
uint32_t version;
|
|
MockFabricPayload fabric_version;
|
|
};
|
|
|
|
struct MockNestedFabricInfo
|
|
{
|
|
amdsmi_bdf_t bdf;
|
|
MockNestedFabricVer fabric_info;
|
|
uint32_t reserved[15];
|
|
};
|
|
|
|
// 27.0 and later: version hoisted to the top level, payload reachable directly.
|
|
struct MockFlatFabricInfo
|
|
{
|
|
amdsmi_bdf_t bdf;
|
|
uint32_t fabric_version;
|
|
MockFabricPayload fabric_info;
|
|
uint32_t reserved[15];
|
|
};
|
|
|
|
// A top-level fabric_version that is not the version number, which is what the
|
|
// name means in the nested shape. Detection keys on the type, not the name.
|
|
struct MockHoistedUnionFabricInfo
|
|
{
|
|
amdsmi_bdf_t bdf;
|
|
MockFabricPayload fabric_version;
|
|
};
|
|
} // namespace
|
|
|
|
TEST(AmdSmiFabricLayoutCompat, DetectsEachShape)
|
|
{
|
|
EXPECT_FALSE(amdSmiFabricInfoIsFlat<MockNestedFabricInfo>::value);
|
|
EXPECT_TRUE(amdSmiFabricInfoIsFlat<MockFlatFabricInfo>::value);
|
|
EXPECT_FALSE(amdSmiFabricInfoIsFlat<MockHoistedUnionFabricInfo>::value);
|
|
}
|
|
|
|
TEST(AmdSmiFabricLayoutCompat, ReadsVersionFromNestedShape)
|
|
{
|
|
MockNestedFabricInfo info{};
|
|
info.fabric_info.version = kAmdSmiFabricVersionUnreported;
|
|
|
|
EXPECT_EQ(amdSmiFabricInfoVersion(info), kAmdSmiFabricVersionUnreported);
|
|
EXPECT_TRUE(amdSmiFabricVersionUsable(amdSmiFabricInfoVersion(info)));
|
|
}
|
|
|
|
TEST(AmdSmiFabricLayoutCompat, ReadsVersionFromFlatShape)
|
|
{
|
|
MockFlatFabricInfo info{};
|
|
info.fabric_version = kAmdSmiFabricVersionUnreported;
|
|
|
|
EXPECT_EQ(amdSmiFabricInfoVersion(info), kAmdSmiFabricVersionUnreported);
|
|
EXPECT_TRUE(amdSmiFabricVersionUsable(amdSmiFabricInfoVersion(info)));
|
|
}
|
|
|
|
TEST(AmdSmiFabricLayoutCompat, ReadsPayloadFromNestedShape)
|
|
{
|
|
MockNestedFabricInfo info{};
|
|
info.fabric_info.fabric_version.v1.fabric_type = AMDSMI_FABRIC_TYPE_UALOE;
|
|
info.fabric_info.fabric_version.v1.accel_state = AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_READY;
|
|
info.fabric_info.fabric_version.v1.accelerator_id = 3;
|
|
|
|
const amdsmi_fabric_info_v1_t* v1 = amdSmiFabricInfoV1(info);
|
|
ASSERT_NE(v1, nullptr);
|
|
EXPECT_EQ(v1->accelerator_id, 3u);
|
|
EXPECT_TRUE(amdSmiFabricStateUsable(v1->fabric_type, v1->accel_state));
|
|
}
|
|
|
|
TEST(AmdSmiFabricLayoutCompat, ReadsPayloadFromFlatShape)
|
|
{
|
|
MockFlatFabricInfo info{};
|
|
info.fabric_info.v1.fabric_type = AMDSMI_FABRIC_TYPE_UALOE;
|
|
info.fabric_info.v1.accel_state = AMDSMI_FABRIC_ACCELERATOR_VPOD_STATE_READY;
|
|
info.fabric_info.v1.accelerator_id = 3;
|
|
|
|
const amdsmi_fabric_info_v1_t* v1 = amdSmiFabricInfoV1(info);
|
|
ASSERT_NE(v1, nullptr);
|
|
EXPECT_EQ(v1->accelerator_id, 3u);
|
|
EXPECT_TRUE(amdSmiFabricStateUsable(v1->fabric_type, v1->accel_state));
|
|
}
|
|
|
|
// The rename did not move anything, which is why one set of ABI asserts covers
|
|
// both headers. If a future layout change breaks this, the accessors are no
|
|
// longer enough on their own.
|
|
TEST(AmdSmiFabricLayoutCompat, BothShapesShareOneAbi)
|
|
{
|
|
EXPECT_EQ(sizeof(MockNestedFabricInfo), sizeof(MockFlatFabricInfo));
|
|
EXPECT_EQ(sizeof(MockFlatFabricInfo), sizeof(amdsmi_fabric_info_t));
|
|
|
|
EXPECT_EQ(offsetof(MockNestedFabricInfo, fabric_info) + offsetof(MockNestedFabricVer, version),
|
|
offsetof(MockFlatFabricInfo, fabric_version));
|
|
EXPECT_EQ(offsetof(MockNestedFabricInfo, fabric_info) + offsetof(MockNestedFabricVer, fabric_version),
|
|
offsetof(MockFlatFabricInfo, fabric_info));
|
|
EXPECT_EQ(offsetof(MockNestedFabricInfo, reserved), offsetof(MockFlatFabricInfo, reserved));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Telemetry name call convention
|
|
//
|
|
// amdsmi_fabric_telem_id_to_string swapped its return value for an out-parameter
|
|
// in 27.0, and we reach it through dlopen, so the version the loaded runtime
|
|
// reports decides how it may be called.
|
|
// ---------------------------------------------------------------------------
|
|
|
|
TEST(AmdSmiFabricTelemAbi, PreflattenRuntimeUsesReturnValue)
|
|
{
|
|
EXPECT_FALSE(amdSmiTelemIdUsesOutParam(26));
|
|
}
|
|
|
|
TEST(AmdSmiFabricTelemAbi, FlattenedRuntimeUsesOutParam)
|
|
{
|
|
EXPECT_TRUE(amdSmiTelemIdUsesOutParam(kAmdSmiTelemOutParamMajor));
|
|
EXPECT_TRUE(amdSmiTelemIdUsesOutParam(kAmdSmiTelemOutParamMajor + 1));
|
|
}
|
|
|
|
// Guessing wrong is not symmetric: the out-parameter form called on an older
|
|
// runtime just leaves the name unwritten, while the older form called on a newer
|
|
// runtime hands it an uninitialized pointer to write through.
|
|
TEST(AmdSmiFabricTelemAbi, UnknownVersionTakesTheSaferConvention)
|
|
{
|
|
EXPECT_TRUE(amdSmiTelemIdUsesOutParam(kAmdSmiLibVersionUnknown));
|
|
}
|
|
|
|
} // namespace RcclUnitTesting
|