diff --git a/babel.so/src/rvs_module.cpp b/babel.so/src/rvs_module.cpp index 8b9dd0971..887b34f1d 100644 --- a/babel.so/src/rvs_module.cpp +++ b/babel.so/src/rvs_module.cpp @@ -75,7 +75,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/gm.so/CMakeLists.txt b/gm.so/CMakeLists.txt index 9d1ab2cef..30dd88344 100644 --- a/gm.so/CMakeLists.txt +++ b/gm.so/CMakeLists.txt @@ -107,19 +107,19 @@ else() set(ASAN_LIB_PATH "$ENV{LD_LIBRARY_PATH}") endif() -if(DEFINED RVS_ROCMSMI) - if(NOT RVS_ROCMSMI EQUAL 1) - if(NOT EXISTS "${ROCM_SMI_LIB_DIR}/lib${ROCM_SMI_LIB}.so") - message("ERROR: rocm_smi library can't be found!...") +if(DEFINED RVS_AMDSMI) + if(NOT RVS_AMDSMI EQUAL 1) + if(NOT EXISTS "${AMD_SMI_LIB_DIR}/lib${AMD_SMI_LIB}.so") + message("ERROR: amd_smi library can't be found!...") RETURN() endif() endif() endif() ## define include directories -include_directories(./ ../ ${ROCM_SMI_INC_DIR} ${YAML_CPP_INCLUDE_DIR}) +include_directories(./ ../ ${AMD_SMI_INC_DIR} ${YAML_CPP_INCLUDE_DIR}) # Add directories to look for library files to link -link_directories(${RVS_LIB_DIR} ${ROCM_SMI_LIB_DIR} ${ASAN_LIB_PATH} ${ROCM_SMI_LIB} ${HIPRAND_LIB_DIR} ${ROCRAND_LIB_DIR} ${HIPBLASLT_LIB_DIR}) +link_directories(${RVS_LIB_DIR} ${AMD_SMI_LIB_DIR} ${ASAN_LIB_PATH} ${AMD_SMI_LIB} ${HIPRAND_LIB_DIR} ${ROCRAND_LIB_DIR} ${HIPBLASLT_LIB_DIR}) ## additional libraries set (PROJECT_LINK_LIBS rvslib libpthread.so libpci.so libm.so) @@ -132,7 +132,7 @@ add_library( ${RVS_TARGET} SHARED ${SOURCES}) set_target_properties(${RVS_TARGET} PROPERTIES SUFFIX .so.${LIB_VERSION_STRING} LIBRARY_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) -target_link_libraries(${RVS_TARGET} ${PROJECT_LINK_LIBS} ${ROCM_SMI_LIB}) +target_link_libraries(${RVS_TARGET} ${PROJECT_LINK_LIBS} ${AMD_SMI_LIB}) add_dependencies(${RVS_TARGET} rvslib) add_custom_command(TARGET ${RVS_TARGET} POST_BUILD diff --git a/gm.so/src/rvs_module.cpp b/gm.so/src/rvs_module.cpp index 7ea8d1a98..d8ea3f278 100644 --- a/gm.so/src/rvs_module.cpp +++ b/gm.so/src/rvs_module.cpp @@ -88,7 +88,7 @@ extern "C" int rvs_module_terminate(void) { pworker = nullptr; } RVSTRACE_ - amdsmi_shut_down(); + return 0; } diff --git a/gpup.so/src/rvs_module.cpp b/gpup.so/src/rvs_module.cpp index 42b2a6ba9..2bec60f64 100644 --- a/gpup.so/src/rvs_module.cpp +++ b/gpup.so/src/rvs_module.cpp @@ -69,7 +69,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/gst.so/src/rvs_module.cpp b/gst.so/src/rvs_module.cpp index 9f285d70d..74612fa54 100644 --- a/gst.so/src/rvs_module.cpp +++ b/gst.so/src/rvs_module.cpp @@ -74,7 +74,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/iet.so/src/rvs_module.cpp b/iet.so/src/rvs_module.cpp index 5511248dd..39f8dc93c 100644 --- a/iet.so/src/rvs_module.cpp +++ b/iet.so/src/rvs_module.cpp @@ -75,7 +75,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/include/gpu_util.h b/include/gpu_util.h index 27dc7154f..70586496c 100644 --- a/include/gpu_util.h +++ b/include/gpu_util.h @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include "amd_smi/amdsmi.h" #define KFD_SYS_PATH_NODES "/sys/class/kfd/kfd/topology/nodes" @@ -50,6 +52,77 @@ extern std::string gpu_get_platform_name (void); namespace rvs { +/** + * @struct GpuInfo + * @brief Unified structure containing all GPU identifiers and properties + * + * This structure to replace the parallel arrays with a single cohesive data structure + * that keeps all GPU-related information together, improving maintainability. + * Older arrays if not updated in lockstep results in wrong behaviour + */ +struct GpuInfo { + uint16_t location_id; //GPU Location ID(location_id) from KFD topology + uint16_t gpu_id; //GPU ID from KFD + uint16_t gpu_idx; // GPU index in system + uint16_t device_id; // PCI Device ID + uint16_t node_id; // HSA Node ID + uint16_t domain_id; // PCI Domain ID + std::string pci_bdf; // PCI Bus:Device.Function string + + GpuInfo() + : location_id(0), gpu_id(0), gpu_idx(0), device_id(0), + node_id(0), domain_id(0), pci_bdf("") {} + + GpuInfo(uint16_t loc_id, uint16_t g_id, uint16_t idx, + uint16_t dev_id, uint16_t n_id, uint16_t dom_id, + const std::string& bdf) + : location_id(loc_id), gpu_id(g_id), gpu_idx(idx), + device_id(dev_id), node_id(n_id), domain_id(dom_id), + pci_bdf(bdf) {} + + bool is_valid() const { + return gpu_id != 0; + } + + std::pair get_domain_location_pair() const { + return std::make_pair(domain_id, location_id); + } +}; +/** + * Error codes for GPU lookup operations + */ +enum class GpuLookupError { + SUCCESS = 0, // Operation successful + NOT_FOUND = -1, // GPU identifier not found + NULL_POINTER = -2, // Null pointer passed as argument + UNINITIALIZED = -3, // GPU list not initialized + INDEX_OUT_OF_BOUNDS = -4, // Internal index corruption + INVALID_ARGUMENT = -5 // Invalid argument value +}; + +/** + * Convert error code to human-readable string + */ +inline const char* error_to_string(GpuLookupError err) { + switch (err) { + case GpuLookupError::SUCCESS: + return "Success"; + case GpuLookupError::NOT_FOUND: + return "GPU identifier not found in system"; + case GpuLookupError::NULL_POINTER: + return "Null pointer passed as argument"; + case GpuLookupError::UNINITIALIZED: + return "GPU list not initialized - call Initialize() first"; + case GpuLookupError::INDEX_OUT_OF_BOUNDS: + return "Internal index corruption detected"; + case GpuLookupError::INVALID_ARGUMENT: + return "Invalid argument value"; + default: + return "Unknown error"; + } +} + + /** * @class gpulist * @@ -57,11 +130,13 @@ namespace rvs { * * Used to quickly get GPU ID from location ID and vs. versa * + * This class maintains a unified list of GPU information and provides + * fast lookups */ class gpulist { public: static int Initialize(); - + static int Shutdown(); static int location2gpu(const uint16_t LocationID, uint16_t* pGpuID); static int gpu2location(const uint16_t GpuID, uint16_t* pLocationID); static int node2gpu(const uint16_t NodeID, uint16_t* pGpuID); @@ -77,7 +152,42 @@ class gpulist { uint16_t* pGPUID); static int node2bdf(const uint16_t NodeID, std::string& pPciBDF); static std::string gpu_get_platform_name (void); + static const GpuInfo* get_gpu_info_by_gpu_id(uint16_t gpu_id); + + static const GpuInfo* get_gpu_info_by_location(uint16_t location_id); + + static const GpuInfo* get_gpu_info_by_node(uint16_t node_id); + + //Get all GPU information + static const std::vector& get_all_gpu_info(); + static bool is_valid_gpu_id(uint16_t gpu_id); + + static size_t get_gpu_count(); + + static void clear(); + protected: + /// Master list of all GPU information + static std::vector gpu_info_list; + + static std::unordered_map gpu_id_to_index;//get index in master list + static std::unordered_map location_id_to_index; + static std::unordered_map node_id_to_index; + + static std::unordered_map device_id_to_index; + + struct PairHash { + template + std::size_t operator()(const std::pair& p) const { + auto h1 = std::hash{}(p.first); + auto h2 = std::hash{}(p.second); + return h1 ^ (h2 << 1); + } + }; + + static std::unordered_map, size_t, PairHash> + domain_location_to_index; + //! Array of GPU location IDs static std::vector location_id; //! Array of GPU IDs @@ -93,6 +203,14 @@ class gpulist { //! Array of PCI BDFs static std::vector pci_bdf; static std::map , uint16_t> domain_loc_map; + +private: + // checkers for sanity + static bool validate_output_pointer(const void* ptr, const char* func_name); + + static bool check_initialized(const char* func_name); + + static bool validate_index(size_t index, const char* func_name); }; } // namespace rvs diff --git a/mem.so/src/rvs_module.cpp b/mem.so/src/rvs_module.cpp index 2c0859ccb..ad77cdd11 100644 --- a/mem.so/src/rvs_module.cpp +++ b/mem.so/src/rvs_module.cpp @@ -85,7 +85,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/pbqt.so/src/rvs_module.cpp b/pbqt.so/src/rvs_module.cpp index da94822eb..a7a6d440c 100644 --- a/pbqt.so/src/rvs_module.cpp +++ b/pbqt.so/src/rvs_module.cpp @@ -76,7 +76,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { rvs::hsa::Terminate(); cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/pebb.so/src/rvs_module.cpp b/pebb.so/src/rvs_module.cpp index cf5dc79c0..93dec955d 100644 --- a/pebb.so/src/rvs_module.cpp +++ b/pebb.so/src/rvs_module.cpp @@ -80,7 +80,7 @@ extern "C" int rvs_module_terminate(void) { rvs::lp::Log("[module_terminate] pebb rvs_module_terminate() - entered", rvs::logtrace); cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/peqt.so/src/rvs_module.cpp b/peqt.so/src/rvs_module.cpp index 1a393168b..af6fbf0af 100644 --- a/peqt.so/src/rvs_module.cpp +++ b/peqt.so/src/rvs_module.cpp @@ -68,7 +68,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/perf.so/src/rvs_module.cpp b/perf.so/src/rvs_module.cpp index d3bb4e136..445c8e0ea 100644 --- a/perf.so/src/rvs_module.cpp +++ b/perf.so/src/rvs_module.cpp @@ -72,7 +72,7 @@ extern "C" int rvs_module_init(void* pMi) { } extern "C" int rvs_module_terminate(void) { - amdsmi_shut_down(); + return 0; } diff --git a/pesm.so/src/rvs_module.cpp b/pesm.so/src/rvs_module.cpp index 4f468c2df..ab3572294 100644 --- a/pesm.so/src/rvs_module.cpp +++ b/pesm.so/src/rvs_module.cpp @@ -93,7 +93,7 @@ extern "C" int rvs_module_terminate(void) { rvs::logtrace); } cleanup_logs(); - amdsmi_shut_down(); + return 0; } diff --git a/rvs/src/rvs_interface.cpp b/rvs/src/rvs_interface.cpp index be112ec8b..76f37776b 100644 --- a/rvs/src/rvs_interface.cpp +++ b/rvs/src/rvs_interface.cpp @@ -27,6 +27,7 @@ #include #include #include +#include "include/gpu_util.h" #include #include @@ -290,6 +291,7 @@ rvs_status_t rvs_terminate(void) { if (RVS_STATE_INITIALIZED != rvs_state) { return RVS_STATUS_INVALID_STATE; } + rvs::gpulist::Shutdown(); rvs_state = RVS_STATE_UNINITIALIZED; return RVS_STATUS_SUCCESS; diff --git a/rvs/test/test_gpu_util.cpp b/rvs/test/test_gpu_util.cpp index 0bc0f569c..1b1066d87 100644 --- a/rvs/test/test_gpu_util.cpp +++ b/rvs/test/test_gpu_util.cpp @@ -1,11 +1,11 @@ /******************************************************************************** * - * Copyright (c) 2018-2022 Advanced Micro Devices, Inc. All rights reserved. + * Copyright (c) 2018-25 Advanced Micro Devices, Inc. All rights reserved. * * MIT LICENSE: * Permission is hereby granted, free of charge, to any person obtaining a copy of * this software and associated documentation files (the "Software"), to deal in - * the Software without result_idtriction, including without limitation the rights to + * the Software without restriction, including without limitation the rights to * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: @@ -22,98 +22,531 @@ * SOFTWARE. * *******************************************************************************/ + #include - +#include +#include +#include #include "gtest/gtest.h" #include "include/gpu_util.h" #include "include/rvs_unit_testing_defs.h" using rvs::gpulist; +using rvs::GpuInfo; +using rvs::GpuLookupError; -class GpuUtilTest : public ::testing::Test , public rvs::gpulist { +/** + * @class GpuUtilTest + * @brief Test fixture for GPU utility functions + * + * This test fixture sets up a mock GPU environment with known values + * for testing all lookup and validation functions. + */ +class GpuUtilTest : public ::testing::Test, public rvs::gpulist { protected: void SetUp() override { + // Clear any existing data + clear(); + + // Set up legacy parallel arrays (for backward compatibility testing) location_id = {2, 1, 5, 7, 8, 3}; gpu_id = {1, 2, 5, 4, 9, 7}; device_id = {3, 0, 2, 7, 5, 1}; node_id = {2, 1, 3, 7, 4, 9}; + domain_id = {0, 0, 0, 0, 0, 0}; + gpu_idx = {0, 1, 2, 3, 4, 5}; + pci_bdf = {"0000:01:00.0", "0000:02:00.0", "0000:03:00.0", + "0000:04:00.0", "0000:05:00.0", "0000:06:00.0"}; + + // Populate new unified structures + gpu_info_list.clear(); + gpu_id_to_index.clear(); + location_id_to_index.clear(); + node_id_to_index.clear(); + device_id_to_index.clear(); + domain_location_to_index.clear(); + + for (size_t i = 0; i < gpu_id.size(); ++i) { + GpuInfo info( + location_id[i], + gpu_id[i], + gpu_idx[i], + device_id[i], + node_id[i], + domain_id[i], + pci_bdf[i] + ); + + gpu_info_list.push_back(info); + + // Build index maps + gpu_id_to_index[gpu_id[i]] = i; + location_id_to_index[location_id[i]] = i; + node_id_to_index[node_id[i]] = i; + device_id_to_index[device_id[i]] = i; + + auto domain_loc_pair = std::make_pair(domain_id[i], location_id[i]); + domain_location_to_index[domain_loc_pair] = i; + } } void TearDown() override { - location_id.clear(); - gpu_id.clear(); - device_id.clear(); - node_id.clear(); + clear(); } }; -TEST_F(GpuUtilTest, gpu_util) { - uint16_t result_id; - int return_value; - // location2gpu - for (int i = 0; i < static_cast(location_id.size()); i++) { - return_value = location2gpu(location_id[i], &result_id); - EXPECT_EQ(result_id, gpu_id[i]); - EXPECT_EQ(return_value, 0); +TEST_F(GpuUtilTest, location2gpu_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < location_id.size(); i++) { + int return_value = location2gpu(location_id[i], &result_id); + EXPECT_EQ(result_id, gpu_id[i]) + << "Location ID " << location_id[i] << " should map to GPU ID " << gpu_id[i]; + EXPECT_EQ(return_value, 0) << "Return value should be 0 (success)"; } - return_value = location2gpu(100, &result_id); - EXPECT_EQ(return_value, -1); +} + +TEST_F(GpuUtilTest, location2gpu_InvalidLookup) { + uint16_t result_id; + int return_value = location2gpu(9999, &result_id); + EXPECT_EQ(return_value, -1) << "Should return -1 for non-existent location ID"; +} - // gpu2location - for (int i = 0; i < static_cast(gpu_id.size()); i++) { - return_value = gpu2location(gpu_id[i], &result_id); - EXPECT_EQ(result_id, location_id[i]); +TEST_F(GpuUtilTest, gpu2location_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < gpu_id.size(); i++) { + int return_value = gpu2location(gpu_id[i], &result_id); + EXPECT_EQ(result_id, location_id[i]) + << "GPU ID " << gpu_id[i] << " should map to Location ID " << location_id[i]; EXPECT_EQ(return_value, 0); } - return_value = gpu2location(100, &result_id); +} + +TEST_F(GpuUtilTest, gpu2location_InvalidLookup) { + uint16_t result_id; + int return_value = gpu2location(9999, &result_id); EXPECT_EQ(return_value, -1); +} - // node2gpu - for (int i = 0; i < static_cast(node_id.size()); i++) { - return_value = node2gpu(node_id[i], &result_id); +TEST_F(GpuUtilTest, node2gpu_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < node_id.size(); i++) { + int return_value = node2gpu(node_id[i], &result_id); EXPECT_EQ(result_id, gpu_id[i]); EXPECT_EQ(return_value, 0); } - return_value = node2gpu(100, &result_id); +} + +TEST_F(GpuUtilTest, node2gpu_InvalidLookup) { + uint16_t result_id; + int return_value = node2gpu(9999, &result_id); EXPECT_EQ(return_value, -1); +} - // location2device - for (int i = 0; i < static_cast(location_id.size()); i++) { - return_value = location2device(location_id[i], &result_id); +TEST_F(GpuUtilTest, location2device_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < location_id.size(); i++) { + int return_value = location2device(location_id[i], &result_id); EXPECT_EQ(result_id, device_id[i]); EXPECT_EQ(return_value, 0); } - return_value = location2device(100, &result_id); +} + +TEST_F(GpuUtilTest, location2device_InvalidLookup) { + uint16_t result_id; + int return_value = location2device(9999, &result_id); EXPECT_EQ(return_value, -1); +} + - // gpu2device - for (int i = 0; i < static_cast(gpu_id.size()); i++) { - return_value = gpu2device(gpu_id[i], &result_id); +TEST_F(GpuUtilTest, gpu2node_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < gpu_id.size(); i++) { + int return_value = gpu2node(gpu_id[i], &result_id); + EXPECT_EQ(result_id, node_id[i]) + << "GPU ID " << gpu_id[i] << " should map to Node ID " << node_id[i]; + EXPECT_EQ(return_value, static_cast(GpuLookupError::SUCCESS)); + } +} + +TEST_F(GpuUtilTest, gpu2node_InvalidGpuId) { + uint16_t result_id; + int return_value = gpu2node(9999, &result_id); + EXPECT_EQ(return_value, static_cast(GpuLookupError::NOT_FOUND)) + << "Should return NOT_FOUND for non-existent GPU ID"; +} + +TEST_F(GpuUtilTest, gpu2node_NullPointer) { + int return_value = gpu2node(gpu_id[0], nullptr); + EXPECT_EQ(return_value, static_cast(GpuLookupError::NULL_POINTER)) + << "Should return NULL_POINTER error when output pointer is null"; +} + +TEST_F(GpuUtilTest, gpu2node_Uninitialized) { + // Clear all data to simulate uninitialized state + gpu_info_list.clear(); + gpu_id_to_index.clear(); + + uint16_t result_id; + int return_value = gpu2node(gpu_id[0], &result_id); + EXPECT_EQ(return_value, static_cast(GpuLookupError::UNINITIALIZED)) + << "Should return UNINITIALIZED when GPU list is empty"; +} + +TEST_F(GpuUtilTest, gpu2device_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < gpu_id.size(); i++) { + int return_value = gpu2device(gpu_id[i], &result_id); EXPECT_EQ(result_id, device_id[i]); - EXPECT_EQ(return_value, 0); + EXPECT_EQ(return_value, static_cast(GpuLookupError::SUCCESS)); } - return_value = gpu2device(100, &result_id); - EXPECT_EQ(return_value, -1); +} - // location2node - for (int i = 0; i < static_cast(location_id.size()); i++) { - return_value = location2node(location_id[i], &result_id); +TEST_F(GpuUtilTest, gpu2device_NullPointer) { + int return_value = gpu2device(gpu_id[0], nullptr); + EXPECT_EQ(return_value, static_cast(GpuLookupError::NULL_POINTER)); +} + +TEST_F(GpuUtilTest, location2node_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < location_id.size(); i++) { + int return_value = location2node(location_id[i], &result_id); EXPECT_EQ(result_id, node_id[i]); - EXPECT_EQ(return_value, 0); + EXPECT_EQ(return_value, static_cast(GpuLookupError::SUCCESS)); } - return_value = location2node(100, &result_id); - EXPECT_EQ(return_value, -1); +} - // gpu2node - for (int i = 0; i < static_cast(gpu_id.size()); i++) { - return_value = gpu2node(gpu_id[i], &result_id); - EXPECT_EQ(result_id, node_id[i]); - EXPECT_EQ(return_value, 0); +TEST_F(GpuUtilTest, location2node_InvalidLocation) { + uint16_t result_id; + int return_value = location2node(9999, &result_id); + EXPECT_EQ(return_value, static_cast(GpuLookupError::NOT_FOUND)); +} + +TEST_F(GpuUtilTest, location2node_NullPointer) { + int return_value = location2node(location_id[0], nullptr); + EXPECT_EQ(return_value, static_cast(GpuLookupError::NULL_POINTER)); +} + + +TEST_F(GpuUtilTest, domlocation2gpu_ValidLookup) { + uint16_t result_id; + for (size_t i = 0; i < gpu_id.size(); i++) { + int return_value = domlocation2gpu(domain_id[i], location_id[i], &result_id); + EXPECT_EQ(result_id, gpu_id[i]) + << "Domain " << domain_id[i] << " + Location " << location_id[i] + << " should map to GPU ID " << gpu_id[i]; + EXPECT_EQ(return_value, static_cast(GpuLookupError::SUCCESS)); } - return_value = gpu2node(100, &result_id); - EXPECT_EQ(return_value, -1); +} + +TEST_F(GpuUtilTest, domlocation2gpu_InvalidPair) { + uint16_t result_id; + int return_value = domlocation2gpu(9999, 9999, &result_id); + EXPECT_EQ(return_value, static_cast(GpuLookupError::NOT_FOUND)); +} + +TEST_F(GpuUtilTest, domlocation2gpu_NullPointer) { + int return_value = domlocation2gpu(domain_id[0], location_id[0], nullptr); + EXPECT_EQ(return_value, static_cast(GpuLookupError::NULL_POINTER)); +} + + + +TEST_F(GpuUtilTest, get_gpu_info_by_gpu_id_ValidLookup) { + for (size_t i = 0; i < gpu_id.size(); i++) { + const GpuInfo* info = get_gpu_info_by_gpu_id(gpu_id[i]); + ASSERT_NE(info, nullptr) << "Should return valid pointer for GPU ID " << gpu_id[i]; + EXPECT_EQ(info->gpu_id, gpu_id[i]); + EXPECT_EQ(info->location_id, location_id[i]); + EXPECT_EQ(info->node_id, node_id[i]); + EXPECT_EQ(info->device_id, device_id[i]); + EXPECT_EQ(info->domain_id, domain_id[i]); + EXPECT_EQ(info->gpu_idx, gpu_idx[i]); + EXPECT_EQ(info->pci_bdf, pci_bdf[i]); + } +} + +TEST_F(GpuUtilTest, get_gpu_info_by_gpu_id_InvalidLookup) { + const GpuInfo* info = get_gpu_info_by_gpu_id(9999); + EXPECT_EQ(info, nullptr) << "Should return nullptr for non-existent GPU ID"; +} + +TEST_F(GpuUtilTest, get_gpu_info_by_location_ValidLookup) { + for (size_t i = 0; i < location_id.size(); i++) { + const GpuInfo* info = get_gpu_info_by_location(location_id[i]); + ASSERT_NE(info, nullptr); + EXPECT_EQ(info->location_id, location_id[i]); + EXPECT_EQ(info->gpu_id, gpu_id[i]); + } +} + +TEST_F(GpuUtilTest, get_gpu_info_by_location_InvalidLookup) { + const GpuInfo* info = get_gpu_info_by_location(9999); + EXPECT_EQ(info, nullptr); +} + +TEST_F(GpuUtilTest, get_gpu_info_by_node_ValidLookup) { + for (size_t i = 0; i < node_id.size(); i++) { + const GpuInfo* info = get_gpu_info_by_node(node_id[i]); + ASSERT_NE(info, nullptr); + EXPECT_EQ(info->node_id, node_id[i]); + EXPECT_EQ(info->gpu_id, gpu_id[i]); + } +} + +TEST_F(GpuUtilTest, get_gpu_info_by_node_InvalidLookup) { + const GpuInfo* info = get_gpu_info_by_node(9999); + EXPECT_EQ(info, nullptr); +} + +TEST_F(GpuUtilTest, get_all_gpu_info) { + const std::vector& all_gpus = get_all_gpu_info(); + EXPECT_EQ(all_gpus.size(), gpu_id.size()) + << "Should return all " << gpu_id.size() << " GPUs"; + + // Verify each GPU info is correct + for (size_t i = 0; i < all_gpus.size(); i++) { + EXPECT_EQ(all_gpus[i].gpu_id, gpu_id[i]); + EXPECT_EQ(all_gpus[i].location_id, location_id[i]); + EXPECT_EQ(all_gpus[i].node_id, node_id[i]); + } +} + +TEST_F(GpuUtilTest, is_valid_gpu_id) { + // Test valid GPU IDs + for (const auto& id : gpu_id) { + EXPECT_TRUE(is_valid_gpu_id(id)) + << "GPU ID " << id << " should be valid"; + } + + // Test invalid GPU ID + EXPECT_FALSE(is_valid_gpu_id(9999)) + << "GPU ID 9999 should be invalid"; +} + +TEST_F(GpuUtilTest, get_gpu_count) { + size_t count = get_gpu_count(); + EXPECT_EQ(count, gpu_id.size()) + << "GPU count should match number of GPUs in test data"; +} + +TEST_F(GpuUtilTest, clear) { + // Verify data exists + EXPECT_GT(get_gpu_count(), 0); + + // Clear all data + clear(); + + // Verify everything is cleared + EXPECT_EQ(get_gpu_count(), 0); + EXPECT_EQ(gpu_info_list.size(), 0); + EXPECT_EQ(gpu_id_to_index.size(), 0); + EXPECT_EQ(location_id_to_index.size(), 0); + EXPECT_EQ(node_id_to_index.size(), 0); + EXPECT_EQ(device_id_to_index.size(), 0); + EXPECT_EQ(domain_location_to_index.size(), 0); + + // Legacy arrays should also be cleared + EXPECT_EQ(location_id.size(), 0); + EXPECT_EQ(gpu_id.size(), 0); + EXPECT_EQ(device_id.size(), 0); + EXPECT_EQ(node_id.size(), 0); +} + + +TEST_F(GpuUtilTest, GpuInfo_DefaultConstructor) { + GpuInfo info; + EXPECT_EQ(info.location_id, 0); + EXPECT_EQ(info.gpu_id, 0); + EXPECT_EQ(info.gpu_idx, 0); + EXPECT_EQ(info.device_id, 0); + EXPECT_EQ(info.node_id, 0); + EXPECT_EQ(info.domain_id, 0); + EXPECT_EQ(info.pci_bdf, ""); +} + +TEST_F(GpuUtilTest, GpuInfo_ParameterizedConstructor) { + GpuInfo info(100, 200, 0, 300, 400, 500, "0000:01:00.0"); + EXPECT_EQ(info.location_id, 100); + EXPECT_EQ(info.gpu_id, 200); + EXPECT_EQ(info.gpu_idx, 0); + EXPECT_EQ(info.device_id, 300); + EXPECT_EQ(info.node_id, 400); + EXPECT_EQ(info.domain_id, 500); + EXPECT_EQ(info.pci_bdf, "0000:01:00.0"); +} + +TEST_F(GpuUtilTest, GpuInfo_is_valid) { + GpuInfo valid_info(1, 100, 0, 2, 3, 4, "0000:01:00.0"); + EXPECT_TRUE(valid_info.is_valid()) + << "GPU info with non-zero gpu_id should be valid"; + + GpuInfo invalid_info; + EXPECT_FALSE(invalid_info.is_valid()) + << "GPU info with zero gpu_id should be invalid"; +} + +TEST_F(GpuUtilTest, GpuInfo_get_domain_location_pair) { + GpuInfo info(100, 200, 0, 300, 400, 500, "0000:01:00.0"); + auto pair = info.get_domain_location_pair(); + EXPECT_EQ(pair.first, 500); // domain_id + EXPECT_EQ(pair.second, 100); // location_id +} + + +TEST_F(GpuUtilTest, Performance_HashMapLookup) { + const int iterations = 10000; + uint16_t result_id; + + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + gpu2node(gpu_id[i % gpu_id.size()], &result_id); + } + auto end = std::chrono::high_resolution_clock::now(); + + auto duration = std::chrono::duration_cast(end - start); + + // Should complete 10,000 lookups in less than 10ms + EXPECT_LT(duration.count(), 10000) + << "10,000 hash map lookups should take less than 10ms, took " + << duration.count() << "µs"; + + std::cout << "Performance: " << iterations << " lookups in " + << duration.count() << "µs (" + << (duration.count() / static_cast(iterations)) + << "µs per lookup)" << std::endl; +} + +TEST_F(GpuUtilTest, Performance_NewAPIvsLegacy) { + const int iterations = 1000; + + // Test new API (get_gpu_info_by_gpu_id) + auto start_new = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + const GpuInfo* info = get_gpu_info_by_gpu_id(gpu_id[i % gpu_id.size()]); + (void)info; // Suppress unused variable warning + } + auto end_new = std::chrono::high_resolution_clock::now(); + auto duration_new = std::chrono::duration_cast( + end_new - start_new); + + // Test legacy API (gpu2node) + uint16_t result; + auto start_legacy = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + gpu2node(gpu_id[i % gpu_id.size()], &result); + } + auto end_legacy = std::chrono::high_resolution_clock::now(); + auto duration_legacy = std::chrono::duration_cast( + end_legacy - start_legacy); + + std::cout << "New API: " << iterations << " lookups in " + << duration_new.count() << "µs" << std::endl; + std::cout << "Legacy API: " << iterations << " lookups in " + << duration_legacy.count() << "µs" << std::endl; + + // Both should be fast (O(1)), but new API might have slight overhead + EXPECT_LT(duration_new.count(), 5000); + EXPECT_LT(duration_legacy.count(), 5000); +} + +TEST_F(GpuUtilTest, EdgeCase_MultipleLookupsWithSameKey) { + uint16_t result1, result2, result3; + + int ret1 = gpu2node(gpu_id[0], &result1); + int ret2 = gpu2node(gpu_id[0], &result2); + int ret3 = gpu2node(gpu_id[0], &result3); + + EXPECT_EQ(ret1, static_cast(GpuLookupError::SUCCESS)); + EXPECT_EQ(ret2, static_cast(GpuLookupError::SUCCESS)); + EXPECT_EQ(ret3, static_cast(GpuLookupError::SUCCESS)); + EXPECT_EQ(result1, result2); + EXPECT_EQ(result2, result3); +} + +TEST_F(GpuUtilTest, EdgeCase_ZeroValues) { + // Add a GPU with zero values (except gpu_id which must be non-zero) + GpuInfo zero_info(0, 12345, 0, 0, 0, 0, ""); + gpu_info_list.push_back(zero_info); + gpu_id_to_index[12345] = gpu_info_list.size() - 1; + + const GpuInfo* info = get_gpu_info_by_gpu_id(12345); + ASSERT_NE(info, nullptr); + EXPECT_EQ(info->location_id, 0); + EXPECT_EQ(info->device_id, 0); + EXPECT_EQ(info->node_id, 0); + EXPECT_TRUE(info->is_valid()); // Still valid because gpu_id is non-zero +} + +TEST_F(GpuUtilTest, EdgeCase_LargeGpuId) { + uint16_t large_id = 0xFFFF; // Max uint16_t + GpuInfo large_info(1, large_id, 0, 2, 3, 4, "0000:ff:00.0"); + gpu_info_list.push_back(large_info); + gpu_id_to_index[large_id] = gpu_info_list.size() - 1; + + uint16_t result; + int ret = gpu2node(large_id, &result); + EXPECT_EQ(ret, static_cast(GpuLookupError::SUCCESS)); +} + +TEST_F(GpuUtilTest, Performance_HashMapVsLinearSearch) { + const int iterations = 10000; + uint16_t result_id; + + // Simulate old linear search + auto old_linear_search = [&](uint16_t id) { + const auto it = std::find(gpu_id.cbegin(), gpu_id.cend(), id); + if (it != gpu_id.cend()) { + size_t pos = std::distance(gpu_id.cbegin(), it); + return node_id[pos]; + } + return uint16_t(0); + }; + + // Test middle element (worst case for linear search) + uint16_t test_id = gpu_id[gpu_id.size() / 2]; + + // Benchmark old method + auto start_old = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + volatile auto result = old_linear_search(test_id); + } + auto end_old = std::chrono::high_resolution_clock::now(); + auto duration_old = std::chrono::duration_cast( + end_old - start_old); + + // Benchmark new method + auto start_new = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < iterations; ++i) { + gpu2node(test_id, &result_id); + } + auto end_new = std::chrono::high_resolution_clock::now(); + auto duration_new = std::chrono::duration_cast( + end_new - start_new); + + // Calculate speedup + double speedup = static_cast(duration_old.count()) / + static_cast(duration_new.count()); + + std::cout << "\n=== Performance Comparison ===\n"; + std::cout << "Iterations: " << iterations << "\n"; + std::cout << "Old (Linear): " << duration_old.count() << " μs\n"; + std::cout << "New (Hash Map): " << duration_new.count() << " μs\n"; + std::cout << "Speedup: " << std::setprecision(2) << speedup << "x\n"; + std::cout << "Improvement: " << std::setprecision(1) + << ((1.0 - 1.0/speedup) * 100.0) << "%\n\n"; + + + + // Verify the hash map structure exists + EXPECT_GT(gpu_id_to_index.size(), 0) << "Hash map should be populated"; +} + + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff --git a/smqt.so/src/rvs_module.cpp b/smqt.so/src/rvs_module.cpp index 7a1fee51d..721dd5dde 100644 --- a/smqt.so/src/rvs_module.cpp +++ b/smqt.so/src/rvs_module.cpp @@ -72,7 +72,7 @@ extern "C" int rvs_module_init(void* pMi) { } extern "C" int rvs_module_terminate(void) { - amdsmi_shut_down(); + return 0; } diff --git a/src/gpu_util.cpp b/src/gpu_util.cpp index 06326b247..2b32009ce 100644 --- a/src/gpu_util.cpp +++ b/src/gpu_util.cpp @@ -48,6 +48,15 @@ std::vector rvs::gpulist::domain_id; std::map , uint16_t> rvs::gpulist::domain_loc_map; std::vector rvs::gpulist::pci_bdf; + +std::vector rvs::gpulist::gpu_info_list; +std::unordered_map rvs::gpulist::gpu_id_to_index; +std::unordered_map rvs::gpulist::location_id_to_index; +std::unordered_map rvs::gpulist::node_id_to_index; +std::unordered_map rvs::gpulist::device_id_to_index; +std::unordered_map, size_t, rvs::gpulist::PairHash> + rvs::gpulist::domain_location_to_index; + const std::map gpu_dev_map = { {0x74a1, "MI300X"}, // MI300X Bare Metal {0x74a9, "MI300X-HF"}, // MI300-HF Bare Metal @@ -506,10 +515,25 @@ int gpu_hip_to_smi_hdl(int hip_index, amdsmi_processor_handle* smi_hdl) { } /** - * @brief Initialize gpulist helper class + * @brief Initialize gpulists helper class * @return 0 if successful, -1 otherwise **/ int rvs::gpulist::Initialize() { + gpu_info_list.clear(); + gpu_id_to_index.clear(); + location_id_to_index.clear(); + node_id_to_index.clear(); + device_id_to_index.clear(); + domain_location_to_index.clear(); + location_id.clear(); + gpu_id.clear(); + gpu_idx.clear(); + device_id.clear(); + node_id.clear(); + domain_id.clear(); + domain_loc_map.clear(); + pci_bdf.clear(); + // inits start here amdsmi_init(AMDSMI_INIT_AMD_GPUS); gpu_get_all_location_id(&location_id); gpu_get_all_gpu_id(&gpu_id); @@ -518,27 +542,95 @@ int rvs::gpulist::Initialize() { gpu_get_all_node_id(&node_id); gpu_get_all_domain_id(&domain_id, domain_loc_map); gpu_get_all_pci_bdf(pci_bdf); + // populate gpu_info_list and hash maps + size_t count = gpu_id.size(); + + // check any mismatches in lists + if (location_id.size() != count || + gpu_idx.size() != count || + device_id.size() != count || + node_id.size() != count || + domain_id.size() != count || + pci_bdf.size() != count) { + std::cerr << "ERROR Initialising device arrays: " << std::endl; + return -1; + } + // update master list + gpu_info_list.reserve(count); + for (size_t i = 0; i < count; ++i) { + GpuInfo info( + location_id[i], + gpu_id[i], + gpu_idx[i], + device_id[i], + node_id[i], + domain_id[i], + pci_bdf[i] + ); + + gpu_info_list.push_back(info); + + // Build index maps for fast lookup[O(1)],instead of std::find(), whihc is O(n) + gpu_id_to_index[gpu_id[i]] = i; + location_id_to_index[location_id[i]] = i; + node_id_to_index[node_id[i]] = i; + device_id_to_index[device_id[i]] = i; + + // Build composite key map, domain and location identifies devices uniquely + auto domain_loc_pair = std::make_pair(domain_id[i], location_id[i]); + domain_location_to_index[domain_loc_pair] = i; + } return 0; } +/** + * @brief Shutdown/cleanup GPU utility resources and amdsmi library + * + * This function should be called once during application shutdown to properly + * release all GPU-related resources and terminate the AMD SMI library. + * It clears all internal data structures and calls amdsmi_shut_down(). + * + * @return 0 if successful, -1 otherwise + */ +int rvs::gpulist::Shutdown() { + clear(); + + amdsmi_status_t ret = amdsmi_shut_down(); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::cerr << "WARNING [gpulist::Shutdown]: amdsmi_shut_down() failed with error code: " + << ret << std::endl; + return -1; + } + + return 0; +} + + + /** * @brief Given Gpu ID return Location ID * @param GpuID Gpu ID * @param pLocationID Location ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ -int rvs::gpulist::gpu2location(const uint16_t GpuID, - uint16_t* pLocationID) { - const auto it = std::find(gpu_id.cbegin(), - gpu_id.cend(), GpuID); - if (it == gpu_id.cend()) { - return -1; - } - size_t pos = std::distance(gpu_id.cbegin(), it); - *pLocationID = location_id[pos]; - return 0; +int rvs::gpulist::gpu2location(const uint16_t GpuID, uint16_t* pLocationID) { + if (!validate_output_pointer(pLocationID, "gpu2location")) + return static_cast(GpuLookupError::NULL_POINTER); + if (!check_initialized("gpu2location")) + return static_cast(GpuLookupError::UNINITIALIZED); + + auto it = gpu_id_to_index.find(GpuID); + if (it == gpu_id_to_index.end()) + return static_cast(GpuLookupError::NOT_FOUND); + + size_t index = it->second; + if (!validate_index(index, "gpu2location")) + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + + *pLocationID = gpu_info_list[index].location_id; + return static_cast(GpuLookupError::SUCCESS); } @@ -546,17 +638,24 @@ int rvs::gpulist::gpu2location(const uint16_t GpuID, * @brief Given Location ID return GPU ID * @param LocationID Location ID of a GPU * @param pGpuID GPU ID of the GPU on Location ID - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ int rvs::gpulist::location2gpu(const uint16_t LocationID, uint16_t* pGpuID) { - const auto it = std::find(location_id.cbegin(), - location_id.cend(), LocationID); - if (it == location_id.cend()) { - return -1; - } - size_t pos = std::distance(location_id.cbegin(), it); - *pGpuID = gpu_id[pos]; - return 0; + if (!validate_output_pointer(pGpuID, "location2gpu")) + return static_cast(GpuLookupError::NULL_POINTER); + if (!check_initialized("location2gpu")) + return static_cast(GpuLookupError::UNINITIALIZED); + + auto it = location_id_to_index.find(LocationID); + if (it == location_id_to_index.end()) + return static_cast(GpuLookupError::NOT_FOUND); + + size_t index = it->second; + if (!validate_index(index, "location2gpu")) + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + + *pGpuID = gpu_info_list[index].gpu_id; + return static_cast(GpuLookupError::SUCCESS); } @@ -564,54 +663,70 @@ int rvs::gpulist::location2gpu(const uint16_t LocationID, uint16_t* pGpuID) { * @brief Given Node ID return GPU ID * @param NodeID Location ID of a GPU * @param pGpuID device ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ int rvs::gpulist::node2gpu(const uint16_t NodeID, uint16_t* pGpuID) { - const auto it = std::find(node_id.cbegin(), - node_id.cend(), NodeID); - if (it == node_id.cend()) { - return -1; - } - size_t pos = std::distance(node_id.cbegin(), it); - *pGpuID = gpu_id[pos]; - return 0; + if (!validate_output_pointer(pGpuID, "node2gpu")) + return static_cast(GpuLookupError::NULL_POINTER); + if (!check_initialized("node2gpu")) + return static_cast(GpuLookupError::UNINITIALIZED); + + auto it = node_id_to_index.find(NodeID); + if (it == node_id_to_index.end()) + return static_cast(GpuLookupError::NOT_FOUND); + + size_t index = it->second; + if (!validate_index(index, "node2gpu")) + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + + *pGpuID = gpu_info_list[index].gpu_id; + return static_cast(GpuLookupError::SUCCESS); } /** * @brief Given Node ID return PCI BDF * @param NodeID GPU Node ID * @param pPciBDF GPU PCI BDF - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ int rvs::gpulist::node2bdf(const uint16_t NodeID, std::string& pPciBDF) { + if (!check_initialized("node2bdf")) + return static_cast(GpuLookupError::UNINITIALIZED); - const auto it = std::find(node_id.cbegin(), - node_id.cend(), NodeID); - if (it == node_id.cend()) { - return -1; - } + auto it = node_id_to_index.find(NodeID); + if (it == node_id_to_index.end()) + return static_cast(GpuLookupError::NOT_FOUND); - size_t pos = std::distance(node_id.cbegin(), it); - pPciBDF = pci_bdf[pos]; - return 0; + size_t index = it->second; + if (!validate_index(index, "node2bdf")) + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + + pPciBDF = gpu_info_list[index].pci_bdf; + return static_cast(GpuLookupError::SUCCESS); } /** * @brief Given Location ID return GPU device ID * @param LocationID Location ID of a GPU * @param pDeviceID device ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ -int rvs::gpulist::location2device(const uint16_t LocationID, - uint16_t* pDeviceID) { - const auto it = std::find(location_id.cbegin(), - location_id.cend(), LocationID); - if (it == location_id.cend()) { - return -1; - } - size_t pos = std::distance(location_id.cbegin(), it); - *pDeviceID = device_id[pos]; - return 0; +int rvs::gpulist::location2device(const uint16_t LocationID, uint16_t* pDeviceID) { + if (!validate_output_pointer(pDeviceID, "location2device")) + return static_cast(GpuLookupError::NULL_POINTER); + if (!check_initialized("location2device")) + return static_cast(GpuLookupError::UNINITIALIZED); + + auto it = location_id_to_index.find(LocationID); + if (it == location_id_to_index.end()) + return static_cast(GpuLookupError::NOT_FOUND); + + size_t index = it->second; + if (!validate_index(index, "location2device")) + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + + *pDeviceID = gpu_info_list[index].device_id; + return static_cast(GpuLookupError::SUCCESS); } @@ -619,54 +734,91 @@ int rvs::gpulist::location2device(const uint16_t LocationID, * @brief Given Gpu ID return GPU device ID * @param GpuID Gpu ID of a GPU * @param pDeviceID device ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ int rvs::gpulist::gpu2device(const uint16_t GpuID, uint16_t* pDeviceID) { - const auto it = std::find(gpu_id.cbegin(), - gpu_id.cend(), GpuID); - if (it == gpu_id.cend()) { - return -1; - } - - size_t pos = std::distance(gpu_id.cbegin(), it); - *pDeviceID = device_id[pos]; - return 0; + if (!validate_output_pointer(pDeviceID, "gpu2device")) { + return static_cast(GpuLookupError::NULL_POINTER); + } + + if (!check_initialized("gpu2device")) { + return static_cast(GpuLookupError::UNINITIALIZED); + } + + auto it = gpu_id_to_index.find(GpuID); + if (it == gpu_id_to_index.end()) { + return static_cast(GpuLookupError::NOT_FOUND); + } + + size_t index = it->second; + if (!validate_index(index, "gpu2device")) { + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + } + + *pDeviceID = gpu_info_list[index].device_id; + return static_cast(GpuLookupError::SUCCESS); } /** * @brief Given Gpu ID return GPU device index * @param GpuID Gpu ID of a GPU * @param pDeviceID device ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ int rvs::gpulist::gpu2gpuindex(const uint16_t GpuID, uint16_t* pGpuIdx) { - const auto it = std::find(gpu_id.cbegin(), - gpu_id.cend(), GpuID); - if (it == gpu_id.cend()) { - return -1; - } - size_t pos = std::distance(gpu_id.cbegin(), it); - *pGpuIdx = gpu_idx[pos]; - return 0; + if (!validate_output_pointer(pGpuIdx, "gpu2gpuindex")) + return static_cast(GpuLookupError::NULL_POINTER); + if (!check_initialized("gpu2gpuindex")) + return static_cast(GpuLookupError::UNINITIALIZED); + + auto it = gpu_id_to_index.find(GpuID); + if (it == gpu_id_to_index.end()) + return static_cast(GpuLookupError::NOT_FOUND); + + size_t index = it->second; + if (!validate_index(index, "gpu2gpuindex")) + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + + *pGpuIdx = gpu_info_list[index].gpu_idx; + return static_cast(GpuLookupError::SUCCESS); } + /** - * @brief Given Gpu ID return GPU HSA Node ID + * @brief Given Gpu ID return GPU HSA node id * @param GpuID Gpu ID of a GPU - * @param pNodeID Node ID of the GPU - * @return 0 if found, -1 otherwise + * @param pNodeID Node ID of the GPU (output) + * @return 0 if found, error code otherwise **/ int rvs::gpulist::gpu2node(const uint16_t GpuID, uint16_t* pNodeID) { - const auto it = std::find(gpu_id.cbegin(), - gpu_id.cend(), GpuID); - if (it == gpu_id.cend()) { - return -1; - } + if (!validate_output_pointer(pNodeID, "gpu2node")) { + return static_cast(GpuLookupError::NULL_POINTER); + } + - size_t pos = std::distance(gpu_id.cbegin(), it); - *pNodeID = node_id[pos]; - return 0; + if (!check_initialized("gpu2node")) { + return static_cast(GpuLookupError::UNINITIALIZED); + } + + auto it = gpu_id_to_index.find(GpuID); + if (it == gpu_id_to_index.end()) { + //std::string msg = "gpu2node: GPU ID 0x" + + // std::to_string(GpuID) + " (decimal: " + std::to_string(GpuID) + + // ") not found in system"; + //std::cerr << "ERROR [GPU_UTIL]: " << msg << std::endl; + return static_cast(GpuLookupError::NOT_FOUND); + } + + + size_t index = it->second; + if (!validate_index(index, "gpu2node")) { + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + } + + *pNodeID = gpu_info_list[index].node_id; + + return static_cast(GpuLookupError::SUCCESS); } @@ -674,26 +826,36 @@ int rvs::gpulist::gpu2node(const uint16_t GpuID, uint16_t* pNodeID) { * @brief Given Location ID return GPU node ID * @param LocationID Location ID of a GPU * @param pNodeID Node ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ -int rvs::gpulist::location2node(const uint16_t LocationID, - uint16_t* pNodeID) { - const auto it = std::find(location_id.cbegin(), - location_id.cend(), LocationID); - if (it == location_id.cend()) { - return -1; - } - - size_t pos = std::distance(location_id.cbegin(), it); - *pNodeID = node_id[pos]; - return 0; +int rvs::gpulist::location2node(const uint16_t LocationID, uint16_t* pNodeID) { + if (!validate_output_pointer(pNodeID, "location2node")) { + return static_cast(GpuLookupError::NULL_POINTER); + } + + if (!check_initialized("location2node")) { + return static_cast(GpuLookupError::UNINITIALIZED); + } + + auto it = location_id_to_index.find(LocationID); + if (it == location_id_to_index.end()) { + return static_cast(GpuLookupError::NOT_FOUND); + } + + size_t index = it->second; + if (!validate_index(index, "location2node")) { + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + } + + *pNodeID = gpu_info_list[index].node_id; + return static_cast(GpuLookupError::SUCCESS); } /** * @brief Given domain id and Location ID return GPU node ID * @param LocationID Location ID of a GPU * @param pNodeID Node ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ int rvs::gpulist::domlocation2node(const uint16_t domainID, const uint16_t LocationID, uint16_t* pNodeID) { @@ -709,33 +871,56 @@ int rvs::gpulist::domlocation2node(const uint16_t domainID, const uint16_t Locat * @brief Given domain id and Location ID return GPU node ID * @param LocationID Location ID of a GPU * @param pNodeID Node ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ -int rvs::gpulist::domlocation2gpu(const uint16_t domainID, const uint16_t LocationID, - uint16_t* pGPUID) { - auto it = domain_loc_map.find(std::make_pair(domainID, LocationID)); - if (it == domain_loc_map.end()) { - return -1; - } - *pGPUID = it->second; - return 0; +int rvs::gpulist::domlocation2gpu(const uint16_t domainID, + const uint16_t LocationID, + uint16_t* pGPUID) { + if (!validate_output_pointer(pGPUID, "domlocation2gpu")) { + return static_cast(GpuLookupError::NULL_POINTER); + } + + if (!check_initialized("domlocation2gpu")) { + return static_cast(GpuLookupError::UNINITIALIZED); + } + + auto key = std::make_pair(domainID, LocationID); + auto it = domain_location_to_index.find(key); + if (it == domain_location_to_index.end()) { + return static_cast(GpuLookupError::NOT_FOUND); + } + + size_t index = it->second; + if (!validate_index(index, "domlocation2gpu")) { + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + } + + *pGPUID = gpu_info_list[index].gpu_id; + return static_cast(GpuLookupError::SUCCESS); } /** * @brief Given Gpu ID return GPU domain ID * @param GpuID Gpu ID of a GPU * @param pDomain domain ID of the GPU - * @return 0 if found, -1 otherwise + * @return 0 if found, errorcode otherwise **/ int rvs::gpulist::gpu2domain(const uint16_t GpuID, uint16_t* pDomain) { - const auto it = std::find(gpu_id.cbegin(), - gpu_id.cend(), GpuID); - if (it == gpu_id.cend()) { - return -1; - } - size_t pos = std::distance(gpu_id.cbegin(), it); - *pDomain = domain_id[pos]; - return 0; + if (!validate_output_pointer(pDomain, "gpu2domain")) + return static_cast(GpuLookupError::NULL_POINTER); + if (!check_initialized("gpu2domain")) + return static_cast(GpuLookupError::UNINITIALIZED); + + auto it = gpu_id_to_index.find(GpuID); + if (it == gpu_id_to_index.end()) + return static_cast(GpuLookupError::NOT_FOUND); + + size_t index = it->second; + if (!validate_index(index, "gpu2domain")) + return static_cast(GpuLookupError::INDEX_OUT_OF_BOUNDS); + + *pDomain = gpu_info_list[index].domain_id; + return static_cast(GpuLookupError::SUCCESS); } /** @@ -758,7 +943,7 @@ bool gpu_check_if_gpu_indexes (const std::vector &index) { /** * @brief Get GPU platform name * @param void - * @return GPU plaform name if found, else null + * @return GPU plaform name if found, else null string **/ std::string rvs::gpulist::gpu_get_platform_name (void) { @@ -783,3 +968,136 @@ std::string rvs::gpulist::gpu_get_platform_name (void) { return it->second; } + +/** + * @brief Get complete GPU information by GPU ID + */ +const rvs::GpuInfo* rvs::gpulist::get_gpu_info_by_gpu_id(uint16_t gpu_id) { + auto it = gpu_id_to_index.find(gpu_id); + if (it == gpu_id_to_index.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= gpu_info_list.size()) { + return nullptr; + } + + return &gpu_info_list[index]; +} + +/** + * @brief Get complete GPU information by location ID + */ +const rvs::GpuInfo* rvs::gpulist::get_gpu_info_by_location(uint16_t location_id) { + auto it = location_id_to_index.find(location_id); + if (it == location_id_to_index.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= gpu_info_list.size()) { + return nullptr; + } + + return &gpu_info_list[index]; +} + +/** + * @brief Get complete GPU information by node ID + */ +const rvs::GpuInfo* rvs::gpulist::get_gpu_info_by_node(uint16_t node_id) { + auto it = node_id_to_index.find(node_id); + if (it == node_id_to_index.end()) { + return nullptr; + } + + size_t index = it->second; + if (index >= gpu_info_list.size()) { + return nullptr; + } + + return &gpu_info_list[index]; +} + +/** + * @brief Get all GPU information + */ +const std::vector& rvs::gpulist::get_all_gpu_info() { + return gpu_info_list; +} + +/** + * @brief Check if GPU ID exists in system + */ +bool rvs::gpulist::is_valid_gpu_id(uint16_t gpu_id) { + return gpu_id_to_index.find(gpu_id) != gpu_id_to_index.end(); +} + +/** + * @brief Get total number of GPUs in system + */ +size_t rvs::gpulist::get_gpu_count() { + return gpu_info_list.size(); +} + +/** + * @brief Clear all GPU information + */ +void rvs::gpulist::clear() { + gpu_info_list.clear(); + gpu_id_to_index.clear(); + location_id_to_index.clear(); + node_id_to_index.clear(); + device_id_to_index.clear(); + domain_location_to_index.clear(); + + location_id.clear(); + gpu_id.clear(); + gpu_idx.clear(); + device_id.clear(); + node_id.clear(); + domain_id.clear(); + domain_loc_map.clear(); + pci_bdf.clear(); +} + + +/** + * @brief Validate result pointer + */ +bool rvs::gpulist::validate_output_pointer(const void* ptr, const char* func_name) { + if (!ptr) { + std::string msg = std::string(func_name) + ": null pointer passed as output parameter"; + std::cerr << "ERROR [GPU_UTIL]: " << msg << std::endl; + return false; + } + return true; +} + +/** + * @brief Check if GPU lists are initialized + */ +bool rvs::gpulist::check_initialized(const char* func_name) { + if (gpu_info_list.empty() || gpu_id_to_index.empty()) { + std::string msg = std::string(func_name) + + ": GPU list not initialized - call Initialize() first"; + std::cerr << "ERROR [GPU_UTIL]: " << msg << std::endl; + return false; + } + return true; +} + +/** + * @brief Validate index bounds + */ +bool rvs::gpulist::validate_index(size_t index, const char* func_name) { + if (index >= gpu_info_list.size()) { + std::string msg = std::string(func_name) + + ": internal index corruption - index " + std::to_string(index) + + " >= " + std::to_string(gpu_info_list.size()); + std::cerr << "ERROR [GPU_UTIL]: " << msg << std::endl; + return false; + } + return true; +} diff --git a/tst.so/src/action.cpp b/tst.so/src/action.cpp index b52220a5a..3f6c7b4c9 100644 --- a/tst.so/src/action.cpp +++ b/tst.so/src/action.cpp @@ -506,7 +506,6 @@ int tst_action::get_all_selected_gpus(void) { msg = "No devices match criteria from the test configuation."; rvs::lp::Err(msg, MODULE_NAME_CAPS, action_name); - //amdsmi_shut_down(); if (bjson) { unsigned int sec; unsigned int usec; @@ -532,7 +531,6 @@ int tst_action::get_all_selected_gpus(void) { tst_res = 0; else tst_res = -1; - //amdsmi_shut_down(); return tst_res; } diff --git a/tst.so/src/rvs_module.cpp b/tst.so/src/rvs_module.cpp index e2b5c453b..0e40cf92d 100644 --- a/tst.so/src/rvs_module.cpp +++ b/tst.so/src/rvs_module.cpp @@ -73,7 +73,7 @@ extern "C" int rvs_module_init(void* pMi) { extern "C" int rvs_module_terminate(void) { cleanup_logs(); - amdsmi_shut_down(); + return 0; }