diff --git a/projects/rdc/CMakeLists.txt b/projects/rdc/CMakeLists.txt index 925ec9851c..03eb7fd13f 100755 --- a/projects/rdc/CMakeLists.txt +++ b/projects/rdc/CMakeLists.txt @@ -115,6 +115,11 @@ option(BUILD_EXAMPLES "Build examples" OFF) # Enable shared libraries for gtest option(BUILD_SHARED_LIBS "Build shared library (.so) or not." ON) +option(BUILD_ESMI "Enable AMDSMI ESMI Library" ON) +if(BUILD_ESMI) + add_definitions("-DENABLE_ESMI_LIB=1") +endif() + # Enable address sanitizer set(ADDRESS_SANITIZER_DEFAULT OFF) if(DEFINED ENV{ADDRESS_SANITIZER}) diff --git a/projects/rdc/CPPLINT.cfg b/projects/rdc/CPPLINT.cfg deleted file mode 100644 index b63692c6df..0000000000 --- a/projects/rdc/CPPLINT.cfg +++ /dev/null @@ -1,3 +0,0 @@ -set noparent -linelength=100 -filter=-build/include_subdir,-legal/copyright,-runtime/printf,-build/c++11,-runtime/int,-build/header_guard diff --git a/projects/rdc/common/rdc_field.data b/projects/rdc/common/rdc_field.data index 8c8603d16b..0bc3350868 100644 --- a/projects/rdc/common/rdc_field.data +++ b/projects/rdc/common/rdc_field.data @@ -215,19 +215,19 @@ FLD_DESC_ENT(RDC_EVNT_XGMI_4_THRPUT, "Tx throughput to XGMI neighbor 4 in b/ FLD_DESC_ENT(RDC_EVNT_XGMI_5_THRPUT, "Tx throughput to XGMI neighbor 5 in b/s", "XGMI_5_T", true) // Asynchronous event notifications -FLD_DESC_ENT(RDC_EVNT_NOTIF_VMFAULT, "VM page fault", "VM_PAGE_FAULT", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_THERMAL_THROTTLE, "Clk freq decrease due to temp", "THERMAL_THROT", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_PRE_RESET, "GPU reset is about to occur", "GPU_PRE_RESET", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_POST_RESET, "GPU reset just occurred", "GPU_POST_RESET", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_MIGRATE_START, "GPU migrate has started", "MIGRATE_START", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_MIGRATE_END, "GPU migrate has ended", "MIGRATE_END", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_PAGE_FAULT_START, "GPU page fault started", "PAGE_FAULT_START", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_PAGE_FAULT_END, "GPU page fault ended", "PAGE_FAULT_END", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_QUEUE_EVICTION, "GPU queue eviction occured", "QUEUE_EVICITION", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_QUEUE_RESTORE, "GPU queue restore occured", "QUEUE_RESTORE", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_UNMAP_FROM_GPU, "GPU unmap occured", "UNMAP_FROM_GPU", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_PROCESS_START, "GPU process started", "PROCESS_START", false) -FLD_DESC_ENT(RDC_EVNT_NOTIF_PROCESS_END, "GPU process ended", "PROCESS_END", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_VMFAULT, "VM page fault", "VM_PAGE_FAULT", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_THERMAL_THROTTLE, "Clk freq decrease due to temp", "THERMAL_THROT", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_PRE_RESET, "GPU reset is about to occur", "GPU_PRE_RESET", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_POST_RESET, "GPU reset just occurred", "GPU_POST_RESET", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_MIGRATE_START, "GPU migrate has started", "MIGRATE_START", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_MIGRATE_END, "GPU migrate has ended", "MIGRATE_END", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_PAGE_FAULT_START, "GPU page fault started", "PAGE_FAULT_START", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_PAGE_FAULT_END, "GPU page fault ended", "PAGE_FAULT_END", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_QUEUE_EVICTION, "GPU queue eviction occured", "QUEUE_EVICITION", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_QUEUE_RESTORE, "GPU queue restore occured", "QUEUE_RESTORE", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_UNMAP_FROM_GPU, "GPU unmap occured", "UNMAP_FROM_GPU", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_PROCESS_START, "GPU process started", "PROCESS_START", false) +FLD_DESC_ENT(RDC_EVNT_NOTIF_PROCESS_END, "GPU process ended", "PROCESS_END", false) // RDC health related fields FLD_DESC_ENT(RDC_HEALTH_XGMI_ERROR, "XGMI one or more errors detected", "XGMI_ERROR", true) @@ -238,3 +238,23 @@ FLD_DESC_ENT(RDC_HEALTH_RETIRED_PAGE_LIMIT, "Retired page limit", FLD_DESC_ENT(RDC_HEALTH_EEPROM_CONFIG_VALID, "Verify checksum of EEPROM", "EEPROM_CONFIG_VALID", true) FLD_DESC_ENT(RDC_HEALTH_POWER_THROTTLE_TIME, "Power throttle status counter", "POWER_THROTTLE_TIME", true) FLD_DESC_ENT(RDC_HEALTH_THERMAL_THROTTLE_TIME, "Total time(ms) in thermal throttle status", "THERMAL_THROTTLE_TIME", true) + +// CPU-related fields description +FLD_DESC_ENT(RDC_FI_CPU_SKT_COUNT, "Number of CPU sockets", "CPU_SKT_COUNT", false) +FLD_DESC_ENT(RDC_FI_CPU_MODEL, "Name of the CPU model", "CPU_MODEL", false) +FLD_DESC_ENT(RDC_FI_CPU_MODEL_ID, "CPU model identifier", "CPU_MODEL_ID", false) +FLD_DESC_ENT(RDC_FI_CPU_FAMILY, "CPU family identifier", "CPU_FAMILY", false) +FLD_DESC_ENT(RDC_FI_CPU_CORES_PER_SKT, "CPU cores per socket identifier", "CPU_CORES_PER_SKT", false) +FLD_DESC_ENT(RDC_FI_CPU_SKT_ENERGY, "CPU socket energy consumption (microjoules)", "CPU_SKT_ENERGY", false) +FLD_DESC_ENT(RDC_FI_CPU_HSMP_DRIVER_VERSION, "HSMP driver version", "CPU_HSMP_DRIVER_VERSION", false) +FLD_DESC_ENT(RDC_FI_CPU_SMU_FW_VERSION, "SMU firmware version", "CPU_SMU_FW_VERSION", false) +FLD_DESC_ENT(RDC_FI_CPU_HSMP_PROTO_VERSION, "HSMP protocol version", "CPU_HSMP_PROTO_VERSION", false) +FLD_DESC_ENT(RDC_FI_CPU_FCLK_FREQUENCY, "CPU fabric clock frequency (MHz)", "CPU_FCLK_FREQUENCY", false) +FLD_DESC_ENT(RDC_FI_CPU_MCLK_FREQUENCY, "CPU memory clock frequency (MHz)", "CPU_MCLK_FREQUENCY", false) +FLD_DESC_ENT(RDC_FI_CPU_CCLK_LIMIT, "CPU core clock limit (MHz)", "CPU_CCLK_LIMIT", false) +FLD_DESC_ENT(RDC_FI_CPU_SKT_ACTIVE_FREQ_LIMIT, "CPU socket active frequency limit (MHz)", "CPU_SKT_ACTIVE_FREQ_LIMIT", false) +FLD_DESC_ENT(RDC_FI_CPU_SKT_FREQ_LIMIT_SRC, "CPU socket frequency limit source type", "CPU_SKT_FREQ_LIMIT_SRC", false) +FLD_DESC_ENT(RDC_FI_CPU_SKT_FREQ_RANGE_MAX, "CPU socket maximum frequency range (MHz)", "CPU_SKT_FREQ_RANGE_MAX", false) +FLD_DESC_ENT(RDC_FI_CPU_SKT_FREQ_RANGE_MIN, "CPU socket minimum frequency range (MHz)", "CPU_SKT_FREQ_RANGE_MIN", false) +FLD_DESC_ENT(RDC_FI_CPU_SKT_C0_RESIDENCY, "CPU socket C0 residency percentage", "CPU_SKT_C0_RESIDENCY", false) +FLD_DESC_ENT(RDC_FI_CPU_SKT_LCLK_DPM_LEVEL, "CPU socket LCLK DPM level", "CPU_SKT_LCLK_DPM_LEVEL", false) diff --git a/projects/rdc/include/rdc/rdc.h b/projects/rdc/include/rdc/rdc.h index 60383f3cc3..33b730add2 100644 --- a/projects/rdc/include/rdc/rdc.h +++ b/projects/rdc/include/rdc/rdc.h @@ -25,10 +25,6 @@ THE SOFTWARE. #ifdef __cplusplus extern "C" { -#endif // __cplusplus - -#ifdef __cplusplus - // cstddef include causes issues on older GCC // use stddef.h instead #if __GNUC__ < 9 @@ -424,6 +420,30 @@ typedef enum { RDC_HEALTH_EEPROM_CONFIG_VALID, //!< Reads the EEPROM and verifies the checksums RDC_HEALTH_POWER_THROTTLE_TIME, //!< Power throttle status counter RDC_HEALTH_THERMAL_THROTTLE_TIME, //!< Total time in thermal throttle status (microseconds) + + /** + * @brief RDC CPU related fields + */ + RDC_FI_CPU_SKT_COUNT = 10000, //!< CPU socket count + RDC_FI_CPU_FIRST = RDC_FI_CPU_SKT_COUNT, + RDC_FI_CPU_MODEL, //!< Name of the model + RDC_FI_CPU_MODEL_ID, //!< CPU model identifier + RDC_FI_CPU_FAMILY, //!< CPU family identifier + RDC_FI_CPU_CORES_PER_SKT, //!< CPU cores per socket + RDC_FI_CPU_SKT_ENERGY, //!< CPU socket energy consumption (microjoules) + RDC_FI_CPU_HSMP_DRIVER_VERSION, //!< HSMP driver version + RDC_FI_CPU_SMU_FW_VERSION, //!< SMU firmware version + RDC_FI_CPU_HSMP_PROTO_VERSION, //!< HSMP protocol version + RDC_FI_CPU_FCLK_FREQUENCY, //!< CPU fabric clock frequency (MHz) + RDC_FI_CPU_MCLK_FREQUENCY, //!< CPU memory clock frequency (MHz) + RDC_FI_CPU_CCLK_LIMIT, //!< CPU core clock limit (MHz) + RDC_FI_CPU_SKT_ACTIVE_FREQ_LIMIT, //!< CPU socket active frequency limit (MHz) + RDC_FI_CPU_SKT_FREQ_LIMIT_SRC, //!< CPU socket frequency limit source type + RDC_FI_CPU_SKT_FREQ_RANGE_MAX, //!< CPU socket maximum frequency range (MHz) + RDC_FI_CPU_SKT_FREQ_RANGE_MIN, //!< CPU socket minimum frequency range (MHz) + RDC_FI_CPU_SKT_C0_RESIDENCY, //!< CPU socket C0 residency percentage + RDC_FI_CPU_SKT_LCLK_DPM_LEVEL, //!< CPU socket LCLK DPM level + RDC_FI_CPU_LAST = RDC_FI_CPU_SKT_LCLK_DPM_LEVEL, } rdc_field_t; // even and odd numbers are used for correctable and uncorrectable errors @@ -1812,15 +1832,15 @@ rdc_status_t rdc_get_num_partition(rdc_handle_t p_rdc_handle, uint32_t index, bool rdc_is_partition_string(const char* s); /** - * @brief Parse partition id into physical gpu and partition + * @brief Parse partition id into socket and partition * * @param[in] s - singular partition string - * @param[out] physicalGpu - socket id + * @param[out] socket - socket id * @param[out] partition - partition id * * @retval bool - success */ -bool rdc_parse_partition_string(const char* s, uint32_t* physicalGpu, uint32_t* partition); +bool rdc_parse_partition_string(const char* s, uint32_t* socket, uint32_t* partition); #ifdef __cplusplus } diff --git a/projects/rdc/include/rdc_lib/RdcEntityCodec.h b/projects/rdc/include/rdc_lib/RdcEntityCodec.h index 2bc596171f..714f354934 100644 --- a/projects/rdc/include/rdc_lib/RdcEntityCodec.h +++ b/projects/rdc/include/rdc_lib/RdcEntityCodec.h @@ -22,8 +22,6 @@ THE SOFTWARE. #ifndef INCLUDE_RDC_LIB_RDCENTITYCODEC_H_ #define INCLUDE_RDC_LIB_RDCENTITYCODEC_H_ -#include "rdc/rdc.h" - /* * * See rdc.h for description of entity_index @@ -36,6 +34,7 @@ THE SOFTWARE. * * */ +#include static constexpr uint32_t RDC_ENTITY_TYPE_SHIFT = 29; static constexpr uint32_t RDC_ENTITY_ROLE_SHIFT = 27; static constexpr uint32_t RDC_ENTITY_INSTANCE_SHIFT = 11; @@ -46,9 +45,5 @@ static constexpr uint32_t RDC_ENTITY_ROLE_MASK = 0x3; // 2 bits for role. static constexpr uint32_t RDC_ENTITY_INSTANCE_MASK = 0x3FF; // 10 bits for instance. static constexpr uint32_t RDC_ENTITY_DEVICE_MASK = 0x3FF; // 10 bits for device. -rdc_entity_info_t rdc_get_info_from_entity_index(uint32_t entity_index); -uint32_t rdc_get_entity_index_from_info(rdc_entity_info_t info); -bool rdc_is_partition_string(const char* s); -bool rdc_parse_partition_string(const char* s, uint32_t* physicalGpu, uint32_t* partition); - +// entity functions are declared in rdc.h #endif // INCLUDE_RDC_LIB_RDCENTITYCODEC_H_ diff --git a/projects/rdc/include/rdc_lib/impl/RdcMetricFetcherImpl.h b/projects/rdc/include/rdc_lib/impl/RdcMetricFetcherImpl.h index 63745a66df..80c1adffb6 100644 --- a/projects/rdc/include/rdc_lib/impl/RdcMetricFetcherImpl.h +++ b/projects/rdc/include/rdc_lib/impl/RdcMetricFetcherImpl.h @@ -91,6 +91,14 @@ class RdcMetricFetcherImpl final : public RdcMetricFetcher { bool async_get_pcie_throughput(uint32_t gpu_index, rdc_field_t field_id, rdc_field_value* value); void get_pcie_throughput(const RdcFieldKey& key); + rdc_status_t fetch_gpu_field_(uint32_t gpu_index, rdc_field_t field_id, rdc_field_value* value, + amdsmi_processor_handle& processor_handle); + rdc_status_t fetch_gpu_partition_field_(uint32_t gpu_index, rdc_field_t field_id, + rdc_field_value* value); + rdc_status_t fetch_cpu_field_(uint32_t gpu_index, rdc_field_t field_id, rdc_field_value* value); + + bool async_fetching = false; + //!< Async metric retreive std::map async_metrics_; std::map> smi_data_; diff --git a/projects/rdc/python_binding/rdc_bootstrap.py b/projects/rdc/python_binding/rdc_bootstrap.py index c7e7c88934..5a9e6d2ece 100644 --- a/projects/rdc/python_binding/rdc_bootstrap.py +++ b/projects/rdc/python_binding/rdc_bootstrap.py @@ -250,6 +250,30 @@ class rdc_field_t(c_int): RDC_HEALTH_POWER_THROTTLE_TIME = 3006 RDC_HEALTH_THERMAL_THROTTLE_TIME = 3007 + RDC_FI_CPU_COUNT = 10000 + RDC_FI_CPU_MODEL = 10001 + RDC_FI_CPU_CORE_ENERGY = 10002 + RDC_FI_CPU_SOCKET_ENERGY = 10003 + RDC_FI_CPU_THREADS_PER_CORE = 10004 + RDC_FI_CPU_HSMP_DRIVER_VERSION = 10005 + RDC_FI_CPU_SMU_FW_VERSION = 10006 + RDC_FI_CPU_HSMP_PROTO_VERSION = 10007 + RDC_FI_CPU_FCLK_FREQUENCY = 10008 + RDC_FI_CPU_MCLK_FREQUENCY = 10009 + RDC_FI_CPU_CCLK_LIMIT = 10010 + RDC_FI_CPU_SOCKET_ACTIVE_FREQ_LIMIT = 10011 + RDC_FI_CPU_SOCKET_FREQ_LIMIT_SRC = 10012 + RDC_FI_CPU_SOCKET_FREQ_RANGE_MAX = 10013 + RDC_FI_CPU_SOCKET_FREQ_RANGE_MIN = 10014 + RDC_FI_CPU_CORE_FREQ_LIMIT = 10015 + RDC_FI_CPU_CORE_BOOST_LIMIT = 10016 + RDC_FI_CPU_SOCKET_C0_RESIDENCY = 10017 + RDC_FI_CPU_SOCKET_LCLK_DPM_LEVEL = 10018 + RDC_FI_CPU_FAMILY = 10019 + RDC_FI_CPU_MODEL_ID = 10020 + RDC_FI_CPU_CORES_PER_SOCKET = 10021 + RDC_FI_CPU_SOCKET_COUNT = 10022 + _rdc_metric_type_lookup = { RDC_FI_INVALID: rdc_metric_type_t.INVALID, RDC_FI_GPU_COUNT: rdc_metric_type_t.LABEL, @@ -277,7 +301,7 @@ class rdc_field_t(c_int): def get_rdc_metric_type(cls, rdc_metric_t): if isinstance(rdc_metric_t, str): rdc_metric_t = getattr(cls, rdc_metric_t, None) - + # If the metric was found, do the lookup, otherwise default GAUGE if rdc_metric_t is not None: return cls._rdc_metric_type_lookup.get(rdc_metric_t, rdc_metric_type_t.GAUGE) @@ -288,7 +312,7 @@ class rdc_field_t(c_int): for attr_name, attr_value in cls.__dict__.items(): if isinstance(attr_value, int) and attr_value == value: return attr_name - return "Unknown field value" + return "Unknown field value" rdc_handle_t = c_void_p rdc_gpu_group_t = c_uint32 diff --git a/projects/rdc/rdc_libs/bootstrap/src/RdcBootStrap.cc b/projects/rdc/rdc_libs/bootstrap/src/RdcBootStrap.cc index 757dd626c2..657584a604 100644 --- a/projects/rdc/rdc_libs/bootstrap/src/RdcBootStrap.cc +++ b/projects/rdc/rdc_libs/bootstrap/src/RdcBootStrap.cc @@ -164,6 +164,7 @@ rdc_status_t rdc_group_gpu_add(rdc_handle_t p_rdc_handle, rdc_gpu_group_t groupI return static_cast(p_rdc_handle)->rdc_group_gpu_add(groupId, gpuIndex); } +// TODO: rewrite get_all to allow different types rdc_status_t rdc_device_get_all(rdc_handle_t p_rdc_handle, uint32_t gpu_index_list[RDC_MAX_NUM_DEVICES], uint32_t* count) { if (!p_rdc_handle || !count) { diff --git a/projects/rdc/rdc_libs/bootstrap/src/RdcEntityCodec.cc b/projects/rdc/rdc_libs/bootstrap/src/RdcEntityCodec.cc index 965cec1ccd..dc9481af4b 100644 --- a/projects/rdc/rdc_libs/bootstrap/src/RdcEntityCodec.cc +++ b/projects/rdc/rdc_libs/bootstrap/src/RdcEntityCodec.cc @@ -24,11 +24,8 @@ THE SOFTWARE. #include #include -#include #include -#include "common/rdc_utils.h" - rdc_entity_info_t rdc_get_info_from_entity_index(uint32_t entity_index) { rdc_entity_info_t info; info.device_type = @@ -54,33 +51,34 @@ bool rdc_is_partition_string(const char* s) { return false; } - if (s[0] != 'g') { + // Support both GPU ('g') and CPU ('c') partition strings + if (s[0] != 'g' && s[0] != 'c') { return false; } std::string str(s); - size_t dotPos = str.find('.'); - if (dotPos == std::string::npos) return false; + size_t dot_pos = str.find('.'); + if (dot_pos == std::string::npos) return false; - if (dotPos <= 1 || dotPos >= str.size() - 1) return false; + if (dot_pos <= 1 || dot_pos >= str.size() - 1) return false; - std::string gpuPart = str.substr(1, dotPos - 1); - std::string partitionPart = str.substr(dotPos + 1); + std::string socket_part = str.substr(1, dot_pos - 1); + std::string partition_part = str.substr(dot_pos + 1); - if (!std::all_of(gpuPart.begin(), gpuPart.end(), ::isdigit) || - !std::all_of(partitionPart.begin(), partitionPart.end(), ::isdigit)) + if (!std::all_of(socket_part.begin(), socket_part.end(), ::isdigit) || + !std::all_of(partition_part.begin(), partition_part.end(), ::isdigit)) return false; - int gpuIndex = std::stoi(gpuPart); - int partitionIndex = std::stoi(partitionPart); + int socket_index = std::stoi(socket_part); + int partition_index = std::stoi(partition_part); - if (gpuIndex < 0 || gpuIndex >= RDC_MAX_NUM_DEVICES) return false; - if (partitionIndex < 0 || partitionIndex >= RDC_MAX_NUM_PARTITIONS) return false; + if (socket_index < 0 || socket_index >= RDC_MAX_NUM_DEVICES) return false; + if (partition_index < 0 || partition_index >= RDC_MAX_NUM_PARTITIONS) return false; return true; } -bool rdc_parse_partition_string(const char* s, uint32_t* physicalGpu, uint32_t* partition) { +bool rdc_parse_partition_string(const char* s, uint32_t* socket, uint32_t* partition) { if (!s) { return false; } @@ -96,16 +94,17 @@ bool rdc_parse_partition_string(const char* s, uint32_t* physicalGpu, uint32_t* if (pos == std::string::npos) return false; - std::string gpuStr = rest.substr(0, pos); - std::string partStr = rest.substr(pos + 1); + std::string socket_str = rest.substr(0, pos); + std::string partition_str = rest.substr(pos + 1); // Ensure both parts are a number - if (!(!gpuStr.empty() && std::all_of(gpuStr.begin(), gpuStr.end(), ::isdigit)) || - !(!partStr.empty() && std::all_of(partStr.begin(), partStr.end(), ::isdigit))) { + if (!(!socket_str.empty() && std::all_of(socket_str.begin(), socket_str.end(), ::isdigit)) || + !(!partition_str.empty() && + std::all_of(partition_str.begin(), partition_str.end(), ::isdigit))) { return false; } - *physicalGpu = std::stoi(gpuStr); - *partition = std::stoi(partStr); + *socket = std::stoi(socket_str); + *partition = std::stoi(partition_str); return true; } diff --git a/projects/rdc/rdc_libs/rdc/src/RdcEmbeddedHandler.cc b/projects/rdc/rdc_libs/rdc/src/RdcEmbeddedHandler.cc index f989345878..0c8fff1dc2 100644 --- a/projects/rdc/rdc_libs/rdc/src/RdcEmbeddedHandler.cc +++ b/projects/rdc/rdc_libs/rdc/src/RdcEmbeddedHandler.cc @@ -49,8 +49,21 @@ class smi_initializer { smi_initializer() { // Make sure smi will not be initialized multiple times amdsmi_shut_down(); - amdsmi_status_t ret = amdsmi_init(AMDSMI_INIT_AMD_GPUS); + amdsmi_status_t ret = AMDSMI_STATUS_UNKNOWN_ERROR; + uint64_t init_flag = AMDSMI_INIT_AMD_GPUS; +#ifdef ENABLE_ESMI_LIB + init_flag |= AMDSMI_INIT_AMD_CPUS; +#endif + ret = amdsmi_init(init_flag); +#ifdef ENABLE_ESMI_LIB if (ret != AMDSMI_STATUS_SUCCESS) { + RDC_LOG(RDC_ERROR, "Failed to initalize amdsmi with CPUs enabled.. Disabling CPUs."); + init_flag &= ~AMDSMI_INIT_AMD_CPUS; + ret = amdsmi_init(init_flag); + } +#endif + if (ret != AMDSMI_STATUS_SUCCESS) { + RDC_LOG(RDC_ERROR, "SMI FAILED with" << ret); throw amd::rdc::RdcException(RDC_ST_FAIL_LOAD_MODULE, "SMI initialize fail"); } } diff --git a/projects/rdc/rdc_libs/rdc/src/RdcGroupSettingsImpl.cc b/projects/rdc/rdc_libs/rdc/src/RdcGroupSettingsImpl.cc index 42fc2b58fb..5aa897218c 100644 --- a/projects/rdc/rdc_libs/rdc/src/RdcGroupSettingsImpl.cc +++ b/projects/rdc/rdc_libs/rdc/src/RdcGroupSettingsImpl.cc @@ -95,8 +95,9 @@ rdc_status_t RdcGroupSettingsImpl::rdc_group_gpu_add(rdc_gpu_group_t groupId, ui } } } else { - if (entity_info.entity_role != RDC_DEVICE_ROLE_PHYSICAL) { - RDC_LOG(RDC_INFO, "GPU " << entity_info.device_index + if ((entity_info.entity_role != RDC_DEVICE_ROLE_PHYSICAL) && + (entity_info.device_type == RDC_DEVICE_TYPE_GPU)) { + RDC_LOG(RDC_INFO, "GPU " << entity_info.device_index << "." << entity_info.device_type << " is not partitionable, but a partition instance was provided."); return RDC_ST_BAD_PARAMETER; } diff --git a/projects/rdc/rdc_libs/rdc/src/RdcMetricFetcherImpl.cc b/projects/rdc/rdc_libs/rdc/src/RdcMetricFetcherImpl.cc index 5a11ec7ed5..732b2f801b 100644 --- a/projects/rdc/rdc_libs/rdc/src/RdcMetricFetcherImpl.cc +++ b/projects/rdc/rdc_libs/rdc/src/RdcMetricFetcherImpl.cc @@ -25,9 +25,8 @@ THE SOFTWARE. #include #include -#include //NOLINT -#include #include +#include #include #include @@ -89,16 +88,16 @@ RdcMetricFetcherImpl::~RdcMetricFetcherImpl() { uint64_t RdcMetricFetcherImpl::now() { struct timeval tv {}; - gettimeofday(&tv, NULL); + gettimeofday(&tv, nullptr); return static_cast(tv.tv_sec) * 1000 + tv.tv_usec / 1000; } void RdcMetricFetcherImpl::get_ecc(uint32_t gpu_index, rdc_field_t field_id, rdc_field_value* value) { amdsmi_status_t err = AMDSMI_STATUS_SUCCESS; - amdsmi_ras_err_state_t err_state; + amdsmi_ras_err_state_t err_state = AMDSMI_RAS_ERR_STATE_INVALID; - amdsmi_processor_handle processor_handle; + amdsmi_processor_handle processor_handle = nullptr; err = get_processor_handle_from_id(gpu_index, &processor_handle); assert(err == AMDSMI_STATUS_SUCCESS); @@ -208,9 +207,9 @@ void RdcMetricFetcherImpl::get_ecc_total(uint32_t gpu_index, rdc_field_t field_i amdsmi_status_t err = AMDSMI_STATUS_SUCCESS; uint64_t correctable_count = 0; uint64_t uncorrectable_count = 0; - amdsmi_ras_err_state_t err_state; + amdsmi_ras_err_state_t err_state = AMDSMI_RAS_ERR_STATE_INVALID; - amdsmi_processor_handle processor_handle; + amdsmi_processor_handle processor_handle = nullptr; err = get_processor_handle_from_id(gpu_index, &processor_handle); if (!value) { @@ -279,10 +278,10 @@ bool RdcMetricFetcherImpl::async_get_pcie_throughput(uint32_t gpu_index, rdc_fie void RdcMetricFetcherImpl::get_pcie_throughput(const RdcFieldKey& key) { uint32_t gpu_index = key.first; - uint64_t sent, received, max_pkt_sz; - amdsmi_status_t ret; + uint64_t sent = 0, received = 0, max_pkt_sz = 0; + amdsmi_status_t ret = AMDSMI_STATUS_INVAL; - amdsmi_processor_handle processor_handle; + amdsmi_processor_handle processor_handle = nullptr; ret = get_processor_handle_from_id(gpu_index, &processor_handle); // Return if the cache does not expire yet @@ -298,8 +297,8 @@ void RdcMetricFetcherImpl::get_pcie_throughput(const RdcFieldKey& key) { ret = amdsmi_get_gpu_pci_throughput(processor_handle, &sent, &received, &max_pkt_sz); uint64_t curTime = now(); - MetricValue value; - value.cache_ttl = 30 * 1000; // cache 30 seconds + MetricValue value{}; + value.cache_ttl = static_cast(30) * 1000; // cache 30 seconds value.value.type = INTEGER; do { std::lock_guard guard(task_mutex_); @@ -370,7 +369,7 @@ rdc_status_t RdcMetricFetcherImpl::bulk_fetch_smi_fields( auto ite = bulk_fields.begin(); for (; ite != bulk_fields.end(); ite++) { amdsmi_gpu_metrics_t gpu_metrics; - amdsmi_processor_handle processor_handle; + amdsmi_processor_handle processor_handle = nullptr; rs = get_processor_handle_from_id(ite->first, &processor_handle); rs = amdsmi_get_gpu_metrics_info(processor_handle, &gpu_metrics); @@ -437,11 +436,11 @@ constexpr double kGig = 1000000000.0; static uint64_t sum_xgmi_read(const amdsmi_gpu_metrics_t& gpu_metrics) { uint64_t total = 0; const auto not_supported_metrics_data = std::numeric_limits::max(); - for (int i = 0; i < AMDSMI_MAX_NUM_XGMI_LINKS; ++i) { - if (gpu_metrics.xgmi_read_data_acc[i] == not_supported_metrics_data) { + for (unsigned long i : gpu_metrics.xgmi_read_data_acc) { + if (i == not_supported_metrics_data) { continue; } - total += gpu_metrics.xgmi_read_data_acc[i]; + total += i; } if (total == 0) { return not_supported_metrics_data; @@ -452,11 +451,11 @@ static uint64_t sum_xgmi_read(const amdsmi_gpu_metrics_t& gpu_metrics) { static uint64_t sum_xgmi_write(const amdsmi_gpu_metrics_t& gpu_metrics) { uint64_t total = 0; const auto not_supported_metrics_data = std::numeric_limits::max(); - for (int i = 0; i < AMDSMI_MAX_NUM_XGMI_LINKS; ++i) { - if (gpu_metrics.xgmi_write_data_acc[i] == not_supported_metrics_data) { + for (unsigned long i : gpu_metrics.xgmi_write_data_acc) { + if (i == not_supported_metrics_data) { continue; } - total += gpu_metrics.xgmi_write_data_acc[i]; + total += i; } if (total == 0) { return not_supported_metrics_data; @@ -464,173 +463,20 @@ static uint64_t sum_xgmi_write(const amdsmi_gpu_metrics_t& gpu_metrics) { return total; } -rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field_t field_id, - rdc_field_value* value) { - if (!value) { - return RDC_ST_BAD_PARAMETER; +std::shared_ptr RdcMetricFetcherImpl::get_smi_data(RdcFieldKey key) { + auto r_info = smi_data_.find(key); + + if (r_info != smi_data_.end()) { + return r_info->second; } - bool async_fetching = false; + return nullptr; +} + +rdc_status_t RdcMetricFetcherImpl::fetch_gpu_field_(uint32_t gpu_index, rdc_field_t field_id, + rdc_field_value* value, + amdsmi_processor_handle& processor_handle) { std::shared_ptr smi_data; - - amdsmi_processor_handle processor_handle = {}; - - rdc_entity_info_t info = rdc_get_info_from_entity_index(gpu_index); - - amdsmi_status_t ret = get_processor_handle_from_id(info.device_index, &processor_handle); - if (ret != AMDSMI_STATUS_SUCCESS) { - std::string info_str; - if (info.entity_role == RDC_DEVICE_ROLE_PARTITION_INSTANCE) { - info_str = - "g" + std::to_string(info.device_index) + "." + std::to_string(info.instance_index); - } else { - info_str = std::to_string(info.device_index); - } - RDC_LOG(RDC_ERROR, "Failed to get processor handle for GPU " << info_str << " error: " << ret); - return Smi2RdcError(ret); - } - - if (!is_field_valid(field_id)) { - RDC_LOG(RDC_ERROR, "Fail to fetch field " << field_id << " which is not supported"); - return RDC_ST_NOT_SUPPORTED; - } - - value->ts = now(); - value->field_id = field_id; - value->status = AMDSMI_STATUS_NOT_SUPPORTED; - - if (info.entity_role == RDC_DEVICE_ROLE_PARTITION_INSTANCE) { - uint16_t num_partitions = 0; - amdsmi_status_t st = get_num_partition(info.device_index, &num_partitions); - if (st != AMDSMI_STATUS_SUCCESS) { - RDC_LOG(RDC_ERROR, "Failed to get partition info for GPU " << info.device_index); - return RDC_ST_UNKNOWN_ERROR; - } - - amdsmi_processor_handle processor_handle = {}; - amdsmi_status_t ret = get_processor_handle_from_id(gpu_index, &processor_handle); - if (ret != AMDSMI_STATUS_SUCCESS) { - RDC_LOG(RDC_ERROR, "Cannot get processor handle for partition " << info.instance_index); - return Smi2RdcError(ret); - } - - amdsmi_gpu_metrics_t gpu_metrics = {}; - ret = amdsmi_get_gpu_metrics_info(processor_handle, &gpu_metrics); - if (ret != AMDSMI_STATUS_SUCCESS) { - RDC_LOG(RDC_ERROR, "Failed to get GPU metrics info for partition " << info.instance_index); - return Smi2RdcError(ret); - } - - switch (field_id) { - case RDC_FI_GPU_CLOCK: { - const uint16_t* clock_array = gpu_metrics.current_gfxclks; - std::vector valid_clocks; - valid_clocks.reserve(AMDSMI_MAX_NUM_GFX_CLKS); - - for (uint32_t i = 0; i < AMDSMI_MAX_NUM_GFX_CLKS; i++) { - uint16_t clk = clock_array[i]; - if (clk != 0 && clk != 0xFFFF) { - valid_clocks.push_back(clk); - } - } - - uint32_t vc = static_cast(valid_clocks.size()); - uint32_t pCount = static_cast(num_partitions); - uint32_t partIdx = info.instance_index; - - if (valid_clocks.empty() || vc < num_partitions) { - RDC_LOG(RDC_ERROR, "No valid clocks, or less than total partitions"); - return RDC_ST_NO_DATA; - } - - if (vc == num_partitions) { - value->value.l_int = static_cast(clock_array[info.instance_index]) * 1000000; - value->type = INTEGER; - value->status = RDC_ST_OK; - return RDC_ST_OK; - } - - uint32_t chunk_size = vc / pCount; - uint32_t start_idx = partIdx * chunk_size; - uint32_t end_idx = start_idx + chunk_size; - - // Average partition clocks - uint64_t sum = 0; - for (uint32_t i = start_idx; i < end_idx; i++) { - sum += valid_clocks[i]; - } - uint32_t count = end_idx - start_idx; - if (count == 0) { - return RDC_ST_NO_DATA; - } - uint64_t avg_clock = sum / count; - - value->value.l_int = avg_clock * 1000000; - value->type = INTEGER; - value->status = RDC_ST_OK; - return RDC_ST_OK; - } - - case RDC_FI_GPU_UTIL: { - uint32_t p = info.instance_index; - if (p >= AMDSMI_MAX_NUM_XCP) { - return RDC_ST_NO_DATA; - } - const amdsmi_gpu_xcp_metrics_t& xcp = gpu_metrics.xcp_stats[p]; - - uint64_t sum = 0; - uint32_t count = 0; - for (uint32_t i = 0; i < AMDSMI_MAX_NUM_XCC; i++) { - uint32_t busy = xcp.gfx_busy_inst[i]; - if (busy != UINT32_MAX) { - sum += busy; - count++; - } - } - if (count == 0) { - return RDC_ST_NO_DATA; - } - uint64_t avg_busy = sum / count; - value->value.l_int = avg_busy; - value->type = INTEGER; - value->status = RDC_ST_OK; - return RDC_ST_OK; - } - - case RDC_FI_GPU_MM_DEC_UTIL: { - uint32_t p = info.instance_index; - if (p >= AMDSMI_MAX_NUM_XCP) { - return RDC_ST_NO_DATA; - } - const amdsmi_gpu_xcp_metrics_t& xcp = gpu_metrics.xcp_stats[p]; - - uint64_t sum = 0; - uint32_t count = 0; - for (uint32_t i = 0; i < AMDSMI_MAX_NUM_VCN; i++) { - uint16_t vcn = xcp.vcn_busy[i]; - if (vcn != UINT16_MAX) { - sum += vcn; - count++; - } - } - if (count == 0) { - return RDC_ST_NO_DATA; - } - uint64_t avg_decode = sum / count; - value->value.l_int = avg_decode; - value->type = INTEGER; - value->status = RDC_ST_OK; - return RDC_ST_OK; - } - - default: - // for now we must let other plugins return valid data for partition metrics - - // TODO: All other fields => N/A for partition IN AMDSMI - // RDC_LOG(RDC_DEBUG, "Partition " << gpu_index << ": Field " << field_id_string(field_id) - // << " not supported => NO_DATA."); - break; - } - } // end if partition + amdsmi_status_t ret = AMDSMI_STATUS_INVAL; auto read_smi_counter = [&](void) { RdcFieldKey f_key(gpu_index, field_id); @@ -645,7 +491,7 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field value->type = INTEGER; }; - auto read_gpu_metrics_uint64_t = [&](void) { + auto read_gpu_metrics_uint64_t = [&]() { amdsmi_gpu_metrics_t gpu_metrics; value->status = amdsmi_get_gpu_metrics_info(processor_handle, &gpu_metrics); RDC_LOG(RDC_DEBUG, "Read the gpu metrics:" << value->status); @@ -766,14 +612,7 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field } break; case RDC_FI_POWER_USAGE: { amdsmi_power_info_t power_info = {}; -// Handle API breaking change in amdsmi commit dc4a16da6fb45d581a6e23c78d340172989418a0 -// Breaking change is only in rocm 6.4.0 (amdsmi 25.2) -// It is reverted to old signature in 6.4.1 (amdsmi 25.3) -#if (((AMDSMI_LIB_VERSION_MAJOR) == 25) && ((AMDSMI_LIB_VERSION_MINOR) == 2)) - value->status = amdsmi_get_power_info(processor_handle, 0, &power_info); -#else value->status = amdsmi_get_power_info(processor_handle, &power_info); -#endif value->type = INTEGER; if (value->status != AMDSMI_STATUS_SUCCESS) { RDC_LOG(RDC_ERROR, "amdsmi_get_power_info failed!"); @@ -854,15 +693,16 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field } break; } - case RDC_FI_GPU_PAGE_RETRIED: - uint32_t num_pages; - amdsmi_retired_page_record_t info; - value->status = amdsmi_get_gpu_bad_page_info(processor_handle, &num_pages, &info); + case RDC_FI_GPU_PAGE_RETRIED: { + uint32_t num_pages = 0; + amdsmi_retired_page_record_t page_record; + value->status = amdsmi_get_gpu_bad_page_info(processor_handle, &num_pages, &page_record); value->type = INTEGER; if (value->status == AMDSMI_STATUS_SUCCESS) { value->value.l_int = num_pages; } break; + } case RDC_FI_OAM_ID: case RDC_FI_DEV_ID: case RDC_FI_REV_ID: @@ -917,7 +757,7 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field constexpr uint32_t kUTILIZATION_COUNTERS(1); amdsmi_utilization_counter_t utilization_counters[kUTILIZATION_COUNTERS]; utilization_counters[0].type = AMDSMI_COARSE_DECODER_ACTIVITY; - uint64_t timestamp; + uint64_t timestamp = 0; value->status = amdsmi_get_utilization_count(processor_handle, utilization_counters, kUTILIZATION_COUNTERS, ×tamp); @@ -1024,18 +864,16 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field case RDC_FI_PCIE_BANDWIDTH: read_gpu_metrics_uint64_t(); break; - case RDC_HEALTH_XGMI_ERROR: { - amdsmi_xgmi_status_t status; - ret = amdsmi_gpu_xgmi_error_status(processor_handle, &status); + amdsmi_xgmi_status_t xgmi_status = AMDSMI_XGMI_STATUS_NO_ERRORS; + ret = amdsmi_gpu_xgmi_error_status(processor_handle, &xgmi_status); value->status = Smi2RdcError(ret); value->type = INTEGER; if (value->status == AMDSMI_STATUS_SUCCESS) { - value->value.l_int = static_cast(status); + value->value.l_int = static_cast(xgmi_status); } break; } - case RDC_HEALTH_PCIE_REPLAY_COUNT: { amdsmi_pcie_info_t pcie_info; ret = amdsmi_get_pcie_info(processor_handle, &pcie_info); @@ -1046,7 +884,6 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field } break; } - case RDC_HEALTH_RETIRED_PAGE_NUM: case RDC_HEALTH_PENDING_PAGE_NUM: { uint32_t num_pages = 0; @@ -1078,7 +915,6 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field } break; } - case RDC_HEALTH_RETIRED_PAGE_LIMIT: { uint32_t retired_page_threshold = 0; ret = amdsmi_get_gpu_bad_page_threshold(processor_handle, &retired_page_threshold); @@ -1089,13 +925,11 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field } break; } - case RDC_HEALTH_EEPROM_CONFIG_VALID: { ret = amdsmi_gpu_validate_ras_eeprom(processor_handle); value->status = Smi2RdcError(ret); break; } - case RDC_HEALTH_POWER_THROTTLE_TIME: case RDC_HEALTH_THERMAL_THROTTLE_TIME: { amdsmi_violation_status_t violation_status; @@ -1110,7 +944,6 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field } break; } - case RDC_FI_GPU_BUSY_PERCENT: { uint32_t gpu_busy_percent = 0; ret = amdsmi_get_gpu_busy_percent(processor_handle, &gpu_busy_percent); @@ -1119,12 +952,415 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field if (value->status == AMDSMI_STATUS_SUCCESS) { value->value.l_int = static_cast(gpu_busy_percent); } + break; + } + default: + break; + } + return Smi2RdcError(static_cast(value->status)); +} + +rdc_status_t RdcMetricFetcherImpl::fetch_gpu_partition_field_(uint32_t gpu_index, + rdc_field_t field_id, + rdc_field_value* value) { + rdc_entity_info_t info = rdc_get_info_from_entity_index(gpu_index); + uint16_t num_partitions = 0; + amdsmi_status_t st = get_num_partition(info.device_index, &num_partitions); + if (st != AMDSMI_STATUS_SUCCESS) { + RDC_LOG(RDC_ERROR, "Failed to get partition info for device " << info.device_index); + return RDC_ST_UNKNOWN_ERROR; + } + + amdsmi_processor_handle processor_handle = {}; + amdsmi_status_t ret = get_processor_handle_from_id(gpu_index, &processor_handle); + if (ret != AMDSMI_STATUS_SUCCESS) { + RDC_LOG(RDC_ERROR, "Cannot get processor handle for partition " << info.instance_index); + return Smi2RdcError(ret); + } + + amdsmi_gpu_metrics_t gpu_metrics = {}; + ret = amdsmi_get_gpu_metrics_info(processor_handle, &gpu_metrics); + if (ret != AMDSMI_STATUS_SUCCESS) { + RDC_LOG(RDC_ERROR, "Failed to get GPU metrics info for partition " << info.instance_index); + return Smi2RdcError(ret); + } + + switch (field_id) { + case RDC_FI_GPU_CLOCK: { + const uint16_t* clock_array = gpu_metrics.current_gfxclks; + std::vector valid_clocks; + // TODO: extract mapping to separate function + valid_clocks.reserve(AMDSMI_MAX_NUM_GFX_CLKS); + for (uint32_t i = 0; i < AMDSMI_MAX_NUM_GFX_CLKS; i++) { + uint16_t clk = clock_array[i]; + if (clk != 0 && clk != 0xFFFF) { + valid_clocks.push_back(clk); + } + } + uint32_t vc = static_cast(valid_clocks.size()); + uint32_t pCount = static_cast(num_partitions); + uint32_t partIdx = info.instance_index; + if (valid_clocks.empty() || vc < num_partitions) { + RDC_LOG(RDC_ERROR, "No valid clocks, or less than total partitions"); + return RDC_ST_NO_DATA; + } + + if (vc == num_partitions) { + value->value.l_int = static_cast(clock_array[info.instance_index]) * 1000000; + value->type = INTEGER; + value->status = RDC_ST_OK; + return RDC_ST_OK; + } + + uint32_t chunk_size = vc / pCount; + uint32_t start_idx = partIdx * chunk_size; + uint32_t end_idx = start_idx + chunk_size; + + // Average partition clocks + uint64_t sum = 0; + for (uint32_t i = start_idx; i < end_idx; i++) { + sum += valid_clocks[i]; + } + uint32_t count = end_idx - start_idx; + if (count == 0) { + return RDC_ST_NO_DATA; + } + uint64_t avg_clock = sum / count; + + value->value.l_int = avg_clock * 1000000; + value->type = INTEGER; + value->status = RDC_ST_OK; + return RDC_ST_OK; + } + case RDC_FI_GPU_UTIL: { + uint32_t p = info.instance_index; + if (p >= AMDSMI_MAX_NUM_XCP) { + return RDC_ST_NO_DATA; + } + const amdsmi_gpu_xcp_metrics_t& xcp = gpu_metrics.xcp_stats[p]; + + uint64_t sum = 0; + uint32_t count = 0; + for (unsigned int busy : xcp.gfx_busy_inst) { + if (busy != UINT32_MAX) { + sum += busy; + count++; + } + } + if (count == 0) { + return RDC_ST_NO_DATA; + } + uint64_t avg_busy = sum / count; + value->value.l_int = avg_busy; + value->type = INTEGER; + value->status = RDC_ST_OK; + return RDC_ST_OK; + } + case RDC_FI_GPU_MM_DEC_UTIL: { + uint32_t p = info.instance_index; + if (p >= AMDSMI_MAX_NUM_XCP) { + return RDC_ST_NO_DATA; + } + const amdsmi_gpu_xcp_metrics_t& xcp = gpu_metrics.xcp_stats[p]; + + uint64_t sum = 0; + uint32_t count = 0; + for (unsigned short vcn : xcp.vcn_busy) { + if (vcn != UINT16_MAX) { + sum += vcn; + count++; + } + } + if (count == 0) { + return RDC_ST_NO_DATA; + } + uint64_t avg_decode = sum / count; + value->value.l_int = avg_decode; + value->type = INTEGER; + value->status = RDC_ST_OK; + return RDC_ST_OK; + } + default: + // All other fields => N/A for partition IN AMDSMI + return RDC_ST_NOT_SUPPORTED; + break; + } +} + +rdc_status_t RdcMetricFetcherImpl::fetch_cpu_field_(uint32_t gpu_index, rdc_field_t field_id, + rdc_field_value* value) { + amdsmi_processor_handle processor_handle = {}; + amdsmi_status_t ret = get_processor_handle_from_id(gpu_index, &processor_handle); + + if (ret != AMDSMI_STATUS_SUCCESS) { + RDC_LOG(RDC_ERROR, "Cannot get processor handle for CPU " << gpu_index); + return Smi2RdcError(ret); + } + + processor_type_t processor_type = AMDSMI_PROCESSOR_TYPE_UNKNOWN; + ret = amdsmi_get_processor_type(processor_handle, &processor_type); + + if (ret != AMDSMI_STATUS_SUCCESS) { + RDC_LOG(RDC_ERROR, "Cannot get processor type for CPU " << gpu_index); + return Smi2RdcError(ret); + } + + RDC_LOG(RDC_DEBUG, "Processor type for CPU " << gpu_index << ": " << processor_type); + + // Initialize common values + value->field_id = field_id; + value->ts = now(); + + switch (field_id) { + case RDC_FI_CPU_MODEL: { + amdsmi_cpu_info_t cpu_info = {}; + value->status = amdsmi_get_cpu_model_name(processor_handle, &cpu_info); + value->type = STRING; + if (value->status == AMDSMI_STATUS_SUCCESS) { + // Use the actual CPU model name from AMD SMI + snprintf(value->value.str, sizeof(value->value.str), "%s", cpu_info.model_name); + } + break; + } + case RDC_FI_CPU_SKT_ENERGY: { + uint64_t energy = 0; + value->status = amdsmi_get_cpu_socket_energy(processor_handle, &energy); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(energy); + } + break; + } + case RDC_FI_CPU_HSMP_DRIVER_VERSION: { + amdsmi_hsmp_driver_version_t hsmp_driver_ver = {}; + value->status = amdsmi_get_cpu_hsmp_driver_version(processor_handle, &hsmp_driver_ver); + value->type = STRING; + if (value->status == AMDSMI_STATUS_SUCCESS) { + snprintf(value->value.str, sizeof(value->value.str), "%u.%u", hsmp_driver_ver.major, + hsmp_driver_ver.minor); + } + break; + } + case RDC_FI_CPU_SMU_FW_VERSION: { + amdsmi_smu_fw_version_t smu_fw = {}; + value->status = amdsmi_get_cpu_smu_fw_version(processor_handle, &smu_fw); + value->type = STRING; + if (value->status == AMDSMI_STATUS_SUCCESS) { + snprintf(value->value.str, sizeof(value->value.str), "%u.%u.%u", smu_fw.major, smu_fw.minor, + smu_fw.debug); + } + break; + } + case RDC_FI_CPU_HSMP_PROTO_VERSION: { + uint32_t proto_ver = 0; + value->status = amdsmi_get_cpu_hsmp_proto_ver(processor_handle, &proto_ver); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(proto_ver); + } + break; + } + case RDC_FI_CPU_FCLK_FREQUENCY: + case RDC_FI_CPU_MCLK_FREQUENCY: { + uint32_t fclk = 0, mclk = 0; + value->status = amdsmi_get_cpu_fclk_mclk(processor_handle, &fclk, &mclk); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + if (field_id == RDC_FI_CPU_FCLK_FREQUENCY) { + value->value.l_int = static_cast(fclk); + } else { + value->value.l_int = static_cast(mclk); + } + } + break; + } + case RDC_FI_CPU_CCLK_LIMIT: { + uint32_t cclk = 0; + value->status = amdsmi_get_cpu_cclk_limit(processor_handle, &cclk); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(cclk); + } + break; + } + case RDC_FI_CPU_SKT_ACTIVE_FREQ_LIMIT: + case RDC_FI_CPU_SKT_FREQ_LIMIT_SRC: { + uint16_t freq = 0; + char* src_type = nullptr; + value->status = + amdsmi_get_cpu_socket_current_active_freq_limit(processor_handle, &freq, &src_type); + if (field_id == RDC_FI_CPU_SKT_ACTIVE_FREQ_LIMIT) { + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(freq); + } + } else { + value->type = STRING; + if (value->status == AMDSMI_STATUS_SUCCESS && src_type != nullptr) { + strncpy(value->value.str, src_type, sizeof(value->value.str) - 1); + value->value.str[sizeof(value->value.str) - 1] = '\0'; + } + } + break; + } + case RDC_FI_CPU_SKT_FREQ_RANGE_MAX: + case RDC_FI_CPU_SKT_FREQ_RANGE_MIN: { + uint16_t fmax = 0, fmin = 0; + value->status = amdsmi_get_cpu_socket_freq_range(processor_handle, &fmax, &fmin); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + if (field_id == RDC_FI_CPU_SKT_FREQ_RANGE_MAX) { + value->value.l_int = static_cast(fmax); + } else { + value->value.l_int = static_cast(fmin); + } + } + break; + } + case RDC_FI_CPU_SKT_C0_RESIDENCY: { + uint32_t c0_residency = 0; + value->status = amdsmi_get_cpu_socket_c0_residency(processor_handle, &c0_residency); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(c0_residency); + } + break; + } + case RDC_FI_CPU_SKT_LCLK_DPM_LEVEL: { + uint8_t nbio_id = 0; + amdsmi_dpm_level_t nbio = {}; + value->status = amdsmi_get_cpu_socket_lclk_dpm_level(processor_handle, nbio_id, &nbio); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(nbio.max_dpm_level); + } + break; + } + case RDC_FI_CPU_FAMILY: { + uint32_t cpu_family = 0; + value->status = amdsmi_get_cpu_family(&cpu_family); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(cpu_family); + } + break; + } + case RDC_FI_CPU_MODEL_ID: { + uint32_t cpu_model = 0; + value->status = amdsmi_get_cpu_model(&cpu_model); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(cpu_model); + } + break; + } + case RDC_FI_CPU_CORES_PER_SKT: { + uint32_t sock_count = 0; + amdsmi_status_t ret_count = amdsmi_get_cpu_socket_count(&sock_count); + if (ret_count == AMDSMI_STATUS_SUCCESS && sock_count > 0) { + std::vector soc_info(sock_count); + value->status = amdsmi_get_cpu_cores_per_socket(sock_count, soc_info.data()); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + // Return cores for the first socket or specific socket based on processor_handle + value->value.l_int = static_cast(soc_info[0].cores_per_socket); + } + } else { + value->status = ret_count; + } + break; + } + case RDC_FI_CPU_SKT_COUNT: { + uint32_t sock_count = 0; + value->status = amdsmi_get_cpu_socket_count(&sock_count); + value->type = INTEGER; + if (value->status == AMDSMI_STATUS_SUCCESS) { + value->value.l_int = static_cast(sock_count); + } + break; } default: + value->status = AMDSMI_STATUS_NOT_SUPPORTED; + RDC_LOG(RDC_DEBUG, "CPU field " << field_id << " not supported"); break; } + return Smi2RdcError(static_cast(value->status)); +} + +rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field_t field_id, + rdc_field_value* value) { + if (!value) { + return RDC_ST_BAD_PARAMETER; + } + + amdsmi_processor_handle processor_handle = {}; + rdc_status_t status = RDC_ST_UNKNOWN_ERROR; + rdc_entity_info_t info = rdc_get_info_from_entity_index(gpu_index); + + amdsmi_status_t ret = get_processor_handle_from_id(info.device_index, &processor_handle); + if (ret != AMDSMI_STATUS_SUCCESS) { + std::string info_str; + if (info.entity_role == RDC_DEVICE_ROLE_PARTITION_INSTANCE) { + info_str = + "g" + std::to_string(info.device_index) + "." + std::to_string(info.instance_index); + } else { + info_str = std::to_string(info.device_index); + } + RDC_LOG(RDC_ERROR, + "Failed to get processor handle for device " << info_str << " error: " << ret); + return Smi2RdcError(ret); + } + + // Check if this is a CPU field being requested + bool is_cpu_field = ((field_id >= RDC_FI_CPU_FIRST) && (field_id <= RDC_FI_CPU_LAST)); + + // If it's a CPU field, validate device type or determine it automatically + if (is_cpu_field) { + // Check if device type is already set correctly + if (info.device_type != RDC_DEVICE_TYPE_CPU) { + // Try to determine if this is actually a CPU device + // In a more complete implementation, you would query the processor type here + processor_type_t processor_type = AMDSMI_PROCESSOR_TYPE_UNKNOWN; + amdsmi_status_t type_ret = amdsmi_get_processor_type(processor_handle, &processor_type); + + if (type_ret == AMDSMI_STATUS_SUCCESS && processor_type == AMDSMI_PROCESSOR_TYPE_AMD_CPU) { + RDC_LOG(RDC_DEBUG, "Auto-detected CPU device for index " << gpu_index); + info.device_type = RDC_DEVICE_TYPE_CPU; + } else { + RDC_LOG(RDC_ERROR, "CPU field " << field_id_string(field_id) + << " requested for non-CPU device " << gpu_index + << " (type: " << info.device_type << ")"); + return RDC_ST_NOT_SUPPORTED; + } + } + } + + if (is_field_valid(field_id) == false) { + RDC_LOG(RDC_ERROR, "Fail to fetch field " << field_id << " which is not supported"); + return RDC_ST_NOT_SUPPORTED; + } + + value->ts = now(); + value->field_id = field_id; + value->status = AMDSMI_STATUS_NOT_SUPPORTED; + if (info.device_type == RDC_DEVICE_TYPE_CPU) { + // don't care about partition for CPUs + status = fetch_cpu_field_(gpu_index, field_id, value); + } else if (info.entity_role == RDC_DEVICE_ROLE_PARTITION_INSTANCE) { + status = fetch_gpu_partition_field_(gpu_index, field_id, value); + } else if (info.device_type == RDC_DEVICE_TYPE_GPU) { + status = fetch_gpu_field_(gpu_index, field_id, value, processor_handle); + } else { + RDC_LOG(RDC_ERROR, "Unsupported device type for fetching field: " << field_id_string(field_id)); + return RDC_ST_NOT_SUPPORTED; + } + + if (status != RDC_ST_OK) { + RDC_LOG(RDC_ERROR, "Fetch status is not ok error: " << status); + } + int64_t latency = now() - value->ts; if (value->status != AMDSMI_STATUS_SUCCESS) { if (async_fetching) { //!< Async fetching is not an error @@ -1148,25 +1384,16 @@ rdc_status_t RdcMetricFetcherImpl::fetch_smi_field(uint32_t gpu_index, rdc_field return value->status == AMDSMI_STATUS_SUCCESS ? RDC_ST_OK : RDC_ST_SMI_ERROR; } -std::shared_ptr RdcMetricFetcherImpl::get_smi_data(RdcFieldKey key) { - std::map>::iterator r_info = smi_data_.find(key); - - if (r_info != smi_data_.end()) { - return r_info->second; - } - return nullptr; -} - static rdc_status_t init_smi_counter(RdcFieldKey fk, amdsmi_event_group_t grp, amdsmi_event_handle_t* handle) { - amdsmi_status_t ret; - uint32_t counters_available; + amdsmi_status_t ret = AMDSMI_STATUS_INVAL; + uint32_t counters_available = 0; uint32_t dv_ind = fk.first; rdc_field_t f = fk.second; assert(handle != nullptr); - amdsmi_processor_handle processor_handle; + amdsmi_processor_handle processor_handle = nullptr; ret = get_processor_handle_from_id(dv_ind, &processor_handle); ret = amdsmi_gpu_counter_group_supported(processor_handle, grp); @@ -1212,7 +1439,7 @@ static rdc_status_t init_smi_counter(RdcFieldKey fk, amdsmi_event_group_t grp, } rdc_status_t RdcMetricFetcherImpl::delete_smi_handle(RdcFieldKey fk) { - amdsmi_status_t ret; + amdsmi_status_t ret = AMDSMI_STATUS_INVAL; switch (fk.second) { case RDC_EVNT_XGMI_0_NOP_TX: @@ -1229,7 +1456,7 @@ rdc_status_t RdcMetricFetcherImpl::delete_smi_handle(RdcFieldKey fk) { case RDC_EVNT_XGMI_3_THRPUT: case RDC_EVNT_XGMI_4_THRPUT: case RDC_EVNT_XGMI_5_THRPUT: { - amdsmi_event_handle_t h; + amdsmi_event_handle_t h = 0L; if (smi_data_.find(fk) == smi_data_.end()) { return RDC_ST_NOT_SUPPORTED; } @@ -1262,8 +1489,8 @@ rdc_status_t RdcMetricFetcherImpl::acquire_smi_handle(RdcFieldKey fk) { rdc_status_t ret = RDC_ST_OK; auto get_evnt_handle = [&](amdsmi_event_group_t grp) { - amdsmi_event_handle_t handle; - rdc_status_t result; + amdsmi_event_handle_t handle = 0L; + rdc_status_t result = RDC_ST_UNKNOWN_ERROR; if (get_smi_data(fk) != nullptr) { // This event has already been initialized. @@ -1275,7 +1502,7 @@ rdc_status_t RdcMetricFetcherImpl::acquire_smi_handle(RdcFieldKey fk) { RDC_LOG(RDC_ERROR, "Failed to init SMI counter. Return:" << result); return result; } - auto fsh = std::shared_ptr(new FieldSMIData); + auto fsh = std::make_shared(); if (fsh == nullptr) { return RDC_ST_INSUFF_RESOURCES; @@ -1299,7 +1526,6 @@ rdc_status_t RdcMetricFetcherImpl::acquire_smi_handle(RdcFieldKey fk) { case RDC_EVNT_XGMI_1_BEATS_TX: ret = get_evnt_handle(AMDSMI_EVNT_GRP_XGMI); break; - case RDC_EVNT_XGMI_0_THRPUT: case RDC_EVNT_XGMI_1_THRPUT: case RDC_EVNT_XGMI_2_THRPUT: @@ -1308,7 +1534,6 @@ rdc_status_t RdcMetricFetcherImpl::acquire_smi_handle(RdcFieldKey fk) { case RDC_EVNT_XGMI_5_THRPUT: ret = get_evnt_handle(AMDSMI_EVNT_GRP_XGMI_DATA_OUT); break; - default: break; } diff --git a/projects/rdc/rdc_libs/rdc/src/RdcSmiLib.cc b/projects/rdc/rdc_libs/rdc/src/RdcSmiLib.cc index ef4d5d53b8..e89ddf2d50 100644 --- a/projects/rdc/rdc_libs/rdc/src/RdcSmiLib.cc +++ b/projects/rdc/rdc_libs/rdc/src/RdcSmiLib.cc @@ -186,9 +186,17 @@ rdc_status_t RdcSmiLib::rdc_telemetry_fields_query(uint32_t field_ids[MAX_NUM_FI RDC_FI_GPU_MEMORY_MAX_BANDWIDTH, RDC_FI_GPU_MEMORY_CUR_BANDWIDTH, RDC_FI_GPU_BUSY_PERCENT, RDC_FI_GPU_PAGE_RETRIED, RDC_FI_DEV_ID, RDC_FI_REV_ID, RDC_FI_TARGET_GRAPHICS_VERSION, - RDC_FI_NUM_OF_COMPUTE_UNITS, RDC_FI_UUID, RDC_FI_GPU_PARTITION_COUNT, + RDC_FI_NUM_OF_COMPUTE_UNITS, RDC_FI_UUID, RDC_FI_GPU_PARTITION_COUNT, + RDC_FI_CPU_SKT_COUNT, RDC_FI_CPU_MODEL, RDC_FI_CPU_MODEL_ID, + RDC_FI_CPU_FAMILY, RDC_FI_CPU_CORES_PER_SKT, RDC_FI_CPU_SKT_ENERGY, + RDC_FI_CPU_HSMP_DRIVER_VERSION, RDC_FI_CPU_SMU_FW_VERSION, RDC_FI_CPU_HSMP_PROTO_VERSION, + RDC_FI_CPU_FCLK_FREQUENCY, RDC_FI_CPU_MCLK_FREQUENCY, RDC_FI_CPU_CCLK_LIMIT, + RDC_FI_CPU_SKT_ACTIVE_FREQ_LIMIT, + RDC_FI_CPU_SKT_FREQ_LIMIT_SRC, RDC_FI_CPU_SKT_FREQ_RANGE_MAX, RDC_FI_CPU_SKT_FREQ_RANGE_MIN, + RDC_FI_CPU_SKT_C0_RESIDENCY, RDC_FI_CPU_SKT_LCLK_DPM_LEVEL, }; // clang-format on + std::copy(fields.begin(), fields.end(), field_ids); *field_count = fields.size(); diff --git a/projects/rdc/rdc_libs/rdc/src/SmiUtils.cc b/projects/rdc/rdc_libs/rdc/src/SmiUtils.cc index 2fb5023d2b..ee4bf1102b 100644 --- a/projects/rdc/rdc_libs/rdc/src/SmiUtils.cc +++ b/projects/rdc/rdc_libs/rdc/src/SmiUtils.cc @@ -28,7 +28,6 @@ THE SOFTWARE. #include "amd_smi/amdsmi.h" #include "rdc/rdc.h" -#include "rdc_lib/RdcLogger.h" namespace amd { namespace rdc { @@ -108,14 +107,6 @@ amdsmi_status_t get_processor_handle_from_id(uint32_t gpu_id, return ret; } - for (auto& proc : procs) { - processor_type_t proc_type = {}; - ret = amdsmi_get_processor_type(proc, &proc_type); - if (proc_type != AMDSMI_PROCESSOR_TYPE_AMD_GPU) { - return AMDSMI_STATUS_NOT_SUPPORTED; - } - } - procs_by_socket[s] = procs; } @@ -169,7 +160,7 @@ amdsmi_status_t get_gpu_id_from_processor_handle(amdsmi_processor_handle process amdsmi_status_t get_processor_count(uint32_t& all_processor_count) { uint32_t total_processor_count = 0; - uint32_t socket_count; + uint32_t socket_count = 0; auto ret = amdsmi_get_socket_handles(&socket_count, nullptr); if (ret != AMDSMI_STATUS_SUCCESS) { return ret; @@ -177,7 +168,7 @@ amdsmi_status_t get_processor_count(uint32_t& all_processor_count) { std::vector sockets(socket_count); ret = amdsmi_get_socket_handles(&socket_count, sockets.data()); for (auto& socket : sockets) { - uint32_t processor_count; + uint32_t processor_count = 0; ret = amdsmi_get_processor_handles(socket, &processor_count, nullptr); if (ret != AMDSMI_STATUS_SUCCESS) { return ret; @@ -234,7 +225,7 @@ amdsmi_status_t get_metrics_info(amdsmi_processor_handle proc, amdsmi_gpu_metric amdsmi_status_t get_num_partition(uint32_t index, uint16_t* num_partition) { // Get the processor handle for the physical device. - amdsmi_processor_handle proc_handle; + amdsmi_processor_handle proc_handle = nullptr; amdsmi_status_t ret = get_processor_handle_from_id(index, &proc_handle); if (ret != AMDSMI_STATUS_SUCCESS) { return ret; diff --git a/projects/rdc/rdc_libs/rdc_modules/rdc_rocp/RdcRocpBase.cc b/projects/rdc/rdc_libs/rdc_modules/rdc_rocp/RdcRocpBase.cc index 1576f0f164..bf2a919355 100644 --- a/projects/rdc/rdc_libs/rdc_modules/rdc_rocp/RdcRocpBase.cc +++ b/projects/rdc/rdc_libs/rdc_modules/rdc_rocp/RdcRocpBase.cc @@ -35,7 +35,6 @@ THE SOFTWARE. #include #include #include -#include #include #include @@ -45,7 +44,6 @@ THE SOFTWARE. #include "rdc_lib/RdcLogger.h" #include "rdc_lib/RdcTelemetryLibInterface.h" #include "rdc_lib/impl/SmiUtils.h" -#include "rdc_lib/rdc_common.h" #include "rdc_modules/rdc_rocp/RdcRocpCounterSampler.h" namespace amd { @@ -157,6 +155,7 @@ const char* RdcRocpBase::get_field_id_from_name(rdc_field_t field) { const std::vector RdcRocpBase::get_field_ids() { std::vector field_ids; + field_ids.reserve(field_to_metric.size()); for (auto& [k, v] : field_to_metric) { field_ids.push_back(k); } @@ -297,6 +296,7 @@ void RdcRocpBase::init_rocp_if_not() { } // populate fields + all_fields.reserve(temp_field_map_k.size()); for (const auto& [k, v] : temp_field_map_k) { all_fields.emplace_back(v); } diff --git a/projects/rdc/rdci/include/RdciDmonSubSystem.h b/projects/rdc/rdci/include/RdciDmonSubSystem.h index bd439bb2dc..04a83ed476 100644 --- a/projects/rdc/rdci/include/RdciDmonSubSystem.h +++ b/projects/rdc/rdci/include/RdciDmonSubSystem.h @@ -43,8 +43,8 @@ class RdciDmonSubSystem : public RdciSubSystem { void show_field_usage() const; void clean_up(); - // Need to resolve gpu indexes after process is called - void resolve_gpu_indexes(); + // Need to resolve device indexes after process is called + void resolve_device_indexes(); void create_temp_group(); void create_temp_field_group(); @@ -67,8 +67,8 @@ class RdciDmonSubSystem : public RdciSubSystem { std::map options_; std::vector field_ids_; - std::string raw_gpu_indexes_; - std::vector gpu_indexes_; + std::string raw_device_indexes_; + std::vector device_indexes_; bool need_cleanup_; uint64_t latest_time_stamp_; bool show_timpstamps_; diff --git a/projects/rdc/rdci/src/RdciDiagSubSystem.cc b/projects/rdc/rdci/src/RdciDiagSubSystem.cc index 7e4b27df7e..3b723f0f41 100644 --- a/projects/rdc/rdci/src/RdciDiagSubSystem.cc +++ b/projects/rdc/rdci/src/RdciDiagSubSystem.cc @@ -177,7 +177,8 @@ void RdciDiagSubSystem::process() { for (uint32_t j = 0; j < test_result.per_gpu_result_count; j++) { const rdc_diag_per_gpu_result_t& gpu_result = test_result.gpu_results[j]; if (strlen(gpu_result.gpu_result.msg) > 0) { - std::cout << " GPU " << gpu_result.gpu_index << " " << gpu_result.gpu_result.msg << "\n"; + // Support both GPU and CPU device result reporting + std::cout << " Device " << gpu_result.gpu_index << " " << gpu_result.gpu_result.msg << "\n"; } } } diff --git a/projects/rdc/rdci/src/RdciDiscoverySubSystem.cc b/projects/rdc/rdci/src/RdciDiscoverySubSystem.cc index c58d5a2d42..38f7b7ce98 100644 --- a/projects/rdc/rdci/src/RdciDiscoverySubSystem.cc +++ b/projects/rdc/rdci/src/RdciDiscoverySubSystem.cc @@ -19,8 +19,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#include "RdciDiscoverySubSystem.h" - #include #include @@ -28,6 +26,7 @@ THE SOFTWARE. #include #include +#include "RdciDiscoverySubSystem.h" #include "rdc/rdc.h" #include "rdc/rdc_private.h" #include "rdc_lib/RdcException.h" @@ -111,6 +110,7 @@ void RdciDiscoverySubSystem::show_help() const { void RdciDiscoverySubSystem::show_attributes() { uint32_t gpu_index_list[RDC_MAX_NUM_DEVICES]; uint32_t count = 0; + rdc_status_t result = rdc_device_get_all(rdc_handle_, gpu_index_list, &count); if (result != RDC_ST_OK) { throw RdcException(result, "Fail to get device information"); @@ -234,14 +234,14 @@ void RdciDiscoverySubSystem::show_attributes_with_partitions() { std::cout << ", \"partitions\": ["; } for (uint32_t pid = 0; pid < num_partition; pid++) { - std::string instance_str = "g" + std::to_string(i) + "." + std::to_string(pid); - + std::string instance_str; rdc_entity_info_t part_info; part_info.device_index = i; part_info.instance_index = pid; part_info.entity_role = RDC_DEVICE_ROLE_PARTITION_INSTANCE; part_info.device_type = RDC_DEVICE_TYPE_GPU; uint32_t part_entity_index = rdc_get_entity_index_from_info(part_info); + instance_str = "g" + std::to_string(i) + "." + std::to_string(pid); rdc_resource_profile_t part_xcc = {}; rdc_resource_profile_t part_decoder = {}; diff --git a/projects/rdc/rdci/src/RdciDmonSubSystem.cc b/projects/rdc/rdci/src/RdciDmonSubSystem.cc index 0cb9d10814..ced78c7220 100644 --- a/projects/rdc/rdci/src/RdciDmonSubSystem.cc +++ b/projects/rdc/rdci/src/RdciDmonSubSystem.cc @@ -26,7 +26,6 @@ THE SOFTWARE. #include #include -#include #include #include #include @@ -35,14 +34,12 @@ THE SOFTWARE. #include #include #include -#include #include #include "common/rdc_fields_supported.h" #include "common/rdc_utils.h" #include "rdc/rdc.h" #include "rdc_lib/RdcException.h" -#include "rdc_lib/rdc_common.h" namespace amd { namespace rdc { @@ -68,7 +65,11 @@ std::string entity_to_string(uint32_t entity_index) { rdc_entity_info_t info = rdc_get_info_from_entity_index(entity_index); if (info.entity_role == RDC_DEVICE_ROLE_PARTITION_INSTANCE) { - return "g" + std::to_string(info.device_index) + "." + std::to_string(info.instance_index); + if (info.device_type == RDC_DEVICE_TYPE_CPU) { + return "c" + std::to_string(info.device_index) + "." + std::to_string(info.instance_index); + } else { + return "g" + std::to_string(info.device_index) + "." + std::to_string(info.instance_index); + } } return std::to_string(info.device_index); } @@ -166,16 +167,15 @@ void RdciDmonSubSystem::parse_cmd_opts(int argc, char** argv) { throw RdcException(RDC_ST_BAD_PARAMETER, "Need to specify the fields or field group id"); } else { std::vector vec_ids = split_string(field_ids, ','); - for (uint32_t i = 0; i < vec_ids.size(); i++) { - if (!IsNumber(vec_ids[i])) { + for (const auto& vec_id : vec_ids) { + if (!IsNumber(vec_id)) { rdc_field_t field_id = RDC_FI_INVALID; - if (!amd::rdc::get_field_id_from_name(vec_ids[i], &field_id)) { - throw RdcException(RDC_ST_BAD_PARAMETER, - "The field name " + vec_ids[i] + " is not valid"); + if (!amd::rdc::get_field_id_from_name(vec_id, &field_id)) { + throw RdcException(RDC_ST_BAD_PARAMETER, "The field name " + vec_id + " is not valid"); } field_ids_.push_back(field_id); } else { - field_ids_.push_back(static_cast(std::stoi(vec_ids[i]))); + field_ids_.push_back(static_cast(std::stoi(vec_id))); } } } @@ -204,7 +204,7 @@ void RdciDmonSubSystem::parse_cmd_opts(int argc, char** argv) { } // Store gpu indexes to parse later - raw_gpu_indexes_ = gpu_indexes; + raw_device_indexes_ = gpu_indexes; } void RdciDmonSubSystem::show_help() const { @@ -254,12 +254,12 @@ void RdciDmonSubSystem::show_help() const { } void RdciDmonSubSystem::create_temp_group() { - if (gpu_indexes_.size() == 0) { + if (device_indexes_.size() == 0) { return; } const std::string group_name("rdci-dmon-group"); - rdc_gpu_group_t group_id; + rdc_gpu_group_t group_id = 0; rdc_status_t result = rdc_group_gpu_create(rdc_handle_, RDC_GROUP_EMPTY, group_name.c_str(), &group_id); if (result != RDC_ST_OK) { @@ -267,17 +267,10 @@ void RdciDmonSubSystem::create_temp_group() { } need_cleanup_ = true; - for (uint32_t i = 0; i < gpu_indexes_.size(); i++) { - result = rdc_group_gpu_add(rdc_handle_, group_id, gpu_indexes_[i]); + for (unsigned int device_index : device_indexes_) { + result = rdc_group_gpu_add(rdc_handle_, group_id, device_index); if (result != RDC_ST_OK) { - rdc_entity_info_t info = rdc_get_info_from_entity_index(gpu_indexes_[i]); - std::string info_str; - if (info.entity_role == RDC_DEVICE_ROLE_PARTITION_INSTANCE) { - info_str = - "g" + std::to_string(info.device_index) + "." + std::to_string(info.instance_index); - } else { - info_str = std::to_string(info.device_index); - } + std::string info_str = entity_to_string(device_index); throw RdcException(result, "Fail to add " + info_str + " to the dmon group."); } } @@ -290,7 +283,7 @@ void RdciDmonSubSystem::create_temp_field_group() { } const std::string field_group_name("rdci-dmon-field-group"); - rdc_field_grp_t group_id; + rdc_field_grp_t group_id = 0; rdc_field_t field_ids[RDC_MAX_FIELD_IDS_PER_FIELD_GROUP]; for (uint32_t i = 0; i < field_ids_.size(); i++) { field_ids[i] = field_ids_[i]; @@ -306,7 +299,7 @@ void RdciDmonSubSystem::create_temp_field_group() { options_.insert({OPTIONS_FIELD_GROUP_ID, group_id}); } -void RdciDmonSubSystem::resolve_gpu_indexes() { +void RdciDmonSubSystem::resolve_device_indexes() { uint32_t device_list[RDC_MAX_NUM_DEVICES]; uint32_t count = 0; rdc_status_t res = rdc_device_get_all(rdc_handle_, device_list, &count); @@ -314,66 +307,105 @@ void RdciDmonSubSystem::resolve_gpu_indexes() { throw RdcException(res, "Failed to get all devices"); } - // If neither group or gpu_index was specified, default to all - if (raw_gpu_indexes_.empty()) { - gpu_indexes_.assign(device_list, device_list + count); + // If neither group or device_index was specified, default to all + if (raw_device_indexes_.empty()) { + device_indexes_.assign(device_list, device_list + count); return; } - std::vector vec_ids = split_string(raw_gpu_indexes_, ','); - for (uint32_t i = 0; i < vec_ids.size(); i++) { - if (rdc_is_partition_string(vec_ids[i].c_str())) { - uint32_t logicalPhysicalGpu; - uint32_t partition; - if (!rdc_parse_partition_string(vec_ids[i].c_str(), &logicalPhysicalGpu, &partition)) { - throw RdcException(RDC_ST_BAD_PARAMETER, "Invalid partition format: " + vec_ids[i]); + std::vector vec_ids = split_string(raw_device_indexes_, ','); + for (const auto& vec_id : vec_ids) { + if (rdc_is_partition_string(vec_id.c_str())) { + uint32_t logicalPhysicalDevice = 0; + uint32_t partition = 0; + if (!rdc_parse_partition_string(vec_id.c_str(), &logicalPhysicalDevice, &partition)) { + throw RdcException(RDC_ST_BAD_PARAMETER, "Invalid partition format: " + vec_id); } - if (logicalPhysicalGpu >= count) { - throw RdcException(RDC_ST_BAD_PARAMETER, - "GPU " + std::to_string(logicalPhysicalGpu) + " is out of range"); + // Detect device type from partition string prefix + bool is_cpu_device = (vec_id[0] == 'c'); + std::string device_type_str = is_cpu_device ? "CPU" : "GPU"; + + if (logicalPhysicalDevice >= count) { + throw RdcException( + RDC_ST_BAD_PARAMETER, + device_type_str + " " + std::to_string(logicalPhysicalDevice) + " is out of range"); } - uint32_t physicalGpu = device_list[logicalPhysicalGpu]; + uint32_t physicalDevice = device_list[logicalPhysicalDevice]; - uint16_t num_partitions = 0; - rdc_status_t st = rdc_get_num_partition(rdc_handle_, physicalGpu, &num_partitions); - if (st != RDC_ST_OK) { - throw RdcException(st, - "Failed to get partition info for GPU " + std::to_string(physicalGpu)); - } - - if (num_partitions == UINT16_MAX || num_partitions <= 1) { + if (is_cpu_device) { + // CPU-specific validation: CPUs don't have GPU-style partitioning if (partition != 0) { - throw RdcException(RDC_ST_BAD_PARAMETER, "GPU " + std::to_string(physicalGpu) + - " is not partitioned, so partition " + - std::to_string(partition) + " is invalid"); + throw RdcException(RDC_ST_BAD_PARAMETER, + "CPU " + std::to_string(physicalDevice) + + " does not support partitioning, partition " + + std::to_string(partition) + " is invalid"); } } else { - if (partition >= num_partitions) { - throw RdcException(RDC_ST_BAD_PARAMETER, - "GPU " + std::to_string(physicalGpu) + " supports only " + - std::to_string(num_partitions) + " partitions, partition " + - std::to_string(partition) + " is invalid"); + // GPU-specific validation: Check GPU partitioning + uint16_t num_partitions = 0; + rdc_status_t st = rdc_get_num_partition(rdc_handle_, physicalDevice, &num_partitions); + if (st != RDC_ST_OK) { + throw RdcException( + st, "Failed to get partition info for GPU " + std::to_string(physicalDevice)); + } + + if (num_partitions == UINT16_MAX || num_partitions <= 1) { + if (partition != 0) { + throw RdcException(RDC_ST_BAD_PARAMETER, "GPU " + std::to_string(physicalDevice) + + " is not partitioned, so partition " + + std::to_string(partition) + " is invalid"); + } + } else { + if (partition >= num_partitions) { + throw RdcException(RDC_ST_BAD_PARAMETER, + "GPU " + std::to_string(physicalDevice) + " supports only " + + std::to_string(num_partitions) + " partitions, partition " + + std::to_string(partition) + " is invalid"); + } } } rdc_entity_info_t phys_info; - phys_info.device_index = physicalGpu; + phys_info.device_index = physicalDevice; phys_info.instance_index = partition; phys_info.entity_role = RDC_DEVICE_ROLE_PARTITION_INSTANCE; - phys_info.device_type = RDC_DEVICE_TYPE_GPU; + phys_info.device_type = is_cpu_device ? RDC_DEVICE_TYPE_CPU : RDC_DEVICE_TYPE_GPU; uint32_t phys_entity_index = rdc_get_entity_index_from_info(phys_info); - gpu_indexes_.push_back(phys_entity_index); - } else if (IsNumber(vec_ids[i])) { - uint32_t logicalIndex = std::stoi(vec_ids[i]); + device_indexes_.push_back(phys_entity_index); + } else if (IsNumber(vec_id)) { + uint32_t logicalIndex = std::stoi(vec_id); if (logicalIndex >= count) { throw RdcException(RDC_ST_BAD_PARAMETER, - "GPU " + std::to_string(logicalIndex) + " is out of range"); + "Device " + std::to_string(logicalIndex) + " is out of range"); } - gpu_indexes_.push_back(std::stoi(vec_ids[i])); + device_indexes_.push_back(std::stoi(vec_id)); + } else if (vec_id.length() >= 2 && vec_id[0] == 'c') { + // Handle CPU device IDs like "c0", "c1", etc. (without partition) + std::string cpu_id = vec_id.substr(1); + if (!IsNumber(cpu_id)) { + throw RdcException(RDC_ST_BAD_PARAMETER, "Invalid CPU device ID: " + vec_id); + } + + uint32_t logicalIndex = std::stoi(cpu_id); + if (logicalIndex >= count) { + throw RdcException(RDC_ST_BAD_PARAMETER, + "CPU " + std::to_string(logicalIndex) + " is out of range"); + } + + uint32_t physicalDevice = device_list[logicalIndex]; + + // Create CPU entity with partition 0 (CPUs don't have partitions) + rdc_entity_info_t cpu_info; + cpu_info.device_index = physicalDevice; + cpu_info.instance_index = 0; // Always 0 for CPUs + cpu_info.entity_role = RDC_DEVICE_ROLE_PARTITION_INSTANCE; + cpu_info.device_type = RDC_DEVICE_TYPE_CPU; + uint32_t cpu_entity_index = rdc_get_entity_index_from_info(cpu_info); + device_indexes_.push_back(cpu_entity_index); } else { - throw RdcException(RDC_ST_BAD_PARAMETER, "The GPU index " + vec_ids[i] + + throw RdcException(RDC_ST_BAD_PARAMETER, "The device index " + vec_id + " needs to be a number or a valid partition"); } } @@ -383,9 +415,9 @@ void RdciDmonSubSystem::show_field_usage() const { std::cout << "Supported fields Ids:" << std::endl; amd::rdc::fld_id2name_map_t& field_id_to_descript = amd::rdc::get_field_id_description_from_id(); - for (auto i = field_id_to_descript.begin(); i != field_id_to_descript.end(); i++) { - if (i->second.do_display || dmon_ops_ == DMON_LIST_ALL_FIELDS) { - std::cout << i->first << " " << i->second.enum_name << " : " << i->second.description << "." + for (const auto& i : field_id_to_descript) { + if (i.second.do_display || dmon_ops_ == DMON_LIST_ALL_FIELDS) { + std::cout << i.first << " " << i.second.enum_name << " : " << i.second.description << "." << std::endl; } } @@ -428,10 +460,10 @@ typedef std::priority_queue, Compa static void collect_new_notifs(rdc_handle_t h, const rdc_group_info_t& group_info, const std::vector& notif_fields, std::vector* notif_ts, field_pq_t* notif_pq) { - rdc_status_t ret; + rdc_status_t ret = RDC_ST_UNKNOWN_ERROR; notif_dev_value value; std::string error_msg; - uint64_t next_ts; + uint64_t next_ts = 0; assert(notif_ts != nullptr); @@ -461,7 +493,7 @@ static void collect_new_notifs(rdc_handle_t h, const rdc_group_info_t& group_inf // ts is milliseconds static std::string ts_string(const time_t ts) { - struct tm* timeinfo; + struct tm* timeinfo = nullptr; time_t tmp_ts = ts / 1000; std::string ret; @@ -504,11 +536,11 @@ void RdciDmonSubSystem::process() { return; } - rdc_status_t result; + rdc_status_t result = RDC_ST_UNKNOWN_ERROR; rdc_group_info_t group_info; rdc_field_group_info_t field_info; - resolve_gpu_indexes(); + resolve_device_indexes(); // Create a temporary group/field if pass as GPU indexes or field ids create_temp_group(); @@ -549,9 +581,9 @@ void RdciDmonSubSystem::process() { // keep extra 1 minute data double max_keep_age = options_[OPTIONS_DELAY] / 1000.0 + 60; const int max_keep_samples = 10; // keep only 10 samples - result = - rdc_field_watch(rdc_handle_, options_[OPTIONS_GROUP_ID], options_[OPTIONS_FIELD_GROUP_ID], - options_[OPTIONS_DELAY] * 1000, max_keep_age, max_keep_samples); + result = rdc_field_watch( + rdc_handle_, options_[OPTIONS_GROUP_ID], options_[OPTIONS_FIELD_GROUP_ID], + static_cast(options_[OPTIONS_DELAY]) * 1000, max_keep_age, max_keep_samples); need_cleanup_ = true; std::stringstream ss; @@ -570,8 +602,8 @@ void RdciDmonSubSystem::process() { ss << std::left << std::setw(25) << "TIMESTAMP"; ss << " "; } - for (uint32_t findex = 0; findex < reg_fields.size(); findex++) { - ss << std::left << std::setw(20) << field_id_string(reg_fields[findex]); + for (auto& reg_field : reg_fields) { + ss << std::left << std::setw(20) << field_id_string(reg_field); } ss << std::endl; @@ -644,7 +676,7 @@ void RdciDmonSubSystem::clean_up() { rdc_field_unwatch(rdc_handle_, options_[OPTIONS_GROUP_ID], options_[OPTIONS_FIELD_GROUP_ID]); } - if (gpu_indexes_.size() != 0) { + if (device_indexes_.size() != 0) { auto group = options_.find(OPTIONS_GROUP_ID); if (group != options_.end()) { rdc_group_gpu_destroy(rdc_handle_, group->second); diff --git a/projects/rdc/rdci/src/RdciFieldGroupSubSystem.cc b/projects/rdc/rdci/src/RdciFieldGroupSubSystem.cc index 3ce66e10f9..c6a6985b92 100644 --- a/projects/rdc/rdci/src/RdciFieldGroupSubSystem.cc +++ b/projects/rdc/rdci/src/RdciFieldGroupSubSystem.cc @@ -167,6 +167,12 @@ void RdciFieldGroupSubSystem::process() { } else { field_ids[i] = static_cast(std::stoi(fields[i])); } + + // Validate field compatibility with available devices + if (!is_field_valid(field_ids[i])) { + throw RdcException(RDC_ST_BAD_PARAMETER, + "Field " + std::to_string(field_ids[i]) + " is not supported"); + } } rdc_field_grp_t group_id; result = rdc_group_field_create(rdc_handle_, fields.size(), &field_ids[0], diff --git a/projects/rdc/tests/rdc_tests/CMakeLists.txt b/projects/rdc/tests/rdc_tests/CMakeLists.txt index 1591c9b43b..b4dc1cab01 100755 --- a/projects/rdc/tests/rdc_tests/CMakeLists.txt +++ b/projects/rdc/tests/rdc_tests/CMakeLists.txt @@ -52,10 +52,22 @@ FetchContent_Declare( FetchContent_MakeAvailable(googletest) # Source files -aux_source_directory(${SRC_DIR} rdctstSources) +set(rdctstSources + ${SRC_DIR}/main.cc + ${SRC_DIR}/test_base.cc + ${SRC_DIR}/test_common.cc + ${SRC_DIR}/test_utils.cc +) -# Other source directories -aux_source_directory(${SRC_DIR}/functional functionalSources) +# Functional test source files +set(functionalSources + ${SRC_DIR}/functional/rdci_discovery.cc + ${SRC_DIR}/functional/rdci_dmon.cc + ${SRC_DIR}/functional/rdci_fieldgroup.cc + ${SRC_DIR}/functional/rdci_group.cc + ${SRC_DIR}/functional/rdci_stats.cc + ${SRC_DIR}/functional/rdci_cpu_support.cc +) link_directories(${ROCM_INSTALL_DIR} ${AMD_SMI_LIB_DIR}) diff --git a/projects/rdc/tests/rdc_tests/functional/rdci_cpu_support.cc b/projects/rdc/tests/rdc_tests/functional/rdci_cpu_support.cc new file mode 100644 index 0000000000..485414c6ba --- /dev/null +++ b/projects/rdc/tests/rdc_tests/functional/rdci_cpu_support.cc @@ -0,0 +1,140 @@ +/* +Copyright (c) 2025 - Advanced Micro Devices, Inc. All rights reserved. + +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 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: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +/** + * @brief Test CPU support infrastructure in RDC + * + * These tests validate the CPU support framework without requiring AMD CPU hardware. + * They focus on infrastructure validation, field definitions, partition string parsing, + * and entity management rather than actual CPU metric collection. + */ + +#include +#include + +#include +#include + +#include "rdc/rdc.h" +#include "rdc_lib/RdcEntityCodec.h" + +class RdcCpuSupportTest : public ::testing::Test { + protected: + void SetUp() override { + rdc_status_t result = rdc_init(0x0); + ASSERT_EQ(result, RDC_ST_OK); + + result = rdc_start_embedded(RDC_OPERATION_MODE_AUTO, &rdc_handle_); + ASSERT_EQ(result, RDC_ST_OK); + ASSERT_NE(rdc_handle_, nullptr); + } + + void TearDown() override { + if (rdc_handle_ != nullptr) { + rdc_stop_embedded(rdc_handle_); + rdc_handle_ = nullptr; + } + rdc_shutdown(); + } + + rdc_handle_t rdc_handle_ = nullptr; +}; + +// Test CPU partition string parsing +TEST_F(RdcCpuSupportTest, CpuPartitionStringParsing) { + // generate map to test this feature + std::map, std::string> cpu_partition_string_map = { + {{0, 0}, "c0.0"}, {{1, 0}, "c1.0"}, {{0, 1}, "c0.1"}, {{2, 0}, "c2.0"}, {{15, 7}, "c15.7"}}; + + for (const auto& [indices, partition_str] : cpu_partition_string_map) { + uint32_t device_index = 255; + uint32_t instance_index = 255; + + bool parsed = rdc_parse_partition_string(partition_str.c_str(), &device_index, &instance_index); + EXPECT_TRUE(parsed) << "Should successfully parse CPU partition string: " << partition_str; + + EXPECT_EQ(device_index, indices.first) + << "Device index should match for partition string: " << partition_str; + EXPECT_EQ(instance_index, indices.second) + << "Instance index should match for partition string: " << partition_str; + } +} + +// Test CPU field group creation +TEST_F(RdcCpuSupportTest, CpuFieldGroupCreation) { + // Test creating field groups with CPU fields + std::vector cpu_fields = {RDC_FI_CPU_SKT_COUNT, RDC_FI_CPU_MODEL}; + + rdc_field_grp_t field_group_id = 0; + const char* group_name = "cpu_test_group"; + + // Create field group with CPU fields + rdc_status_t result = + rdc_group_field_create(rdc_handle_, static_cast(cpu_fields.size()), &cpu_fields[0], + group_name, &field_group_id); + + // Should succeed in creating CPU field group + EXPECT_EQ(result, RDC_ST_OK) << "Should be able to create CPU field group"; + + if (result == RDC_ST_OK) { + // Verify we can get field group info + rdc_field_group_info_t group_info; + rdc_status_t info_result = rdc_group_field_get_info(rdc_handle_, field_group_id, &group_info); + + EXPECT_EQ(info_result, RDC_ST_OK) << "Should be able to get field group info"; + + if (info_result == RDC_ST_OK) { + EXPECT_EQ(group_info.count, cpu_fields.size()) << "Field group should have correct count"; + EXPECT_STREQ(group_info.group_name, group_name) << "Field group should have correct name"; + } + + // Clean up + rdc_group_field_destroy(rdc_handle_, field_group_id); + } +} + +// Test mixed GPU/CPU field groups +TEST_F(RdcCpuSupportTest, MixedGpuCpuFieldGroups) { + // Test creating field groups with both GPU and CPU fields + std::vector mixed_fields = { + RDC_FI_GPU_UTIL, // GPU field + RDC_FI_CPU_CCLK_LIMIT, // CPU field + RDC_FI_GPU_TEMP, // GPU field + RDC_FI_CPU_FCLK_FREQUENCY // CPU field + }; + + rdc_field_grp_t field_group_id = 0; + const char* group_name = "mixed_test_group"; + + // Create field group with mixed GPU/CPU fields + rdc_status_t result = + rdc_group_field_create(rdc_handle_, static_cast(mixed_fields.size()), + &mixed_fields[0], group_name, &field_group_id); + + // Should succeed in creating mixed field group + EXPECT_EQ(result, RDC_ST_OK) << "Should be able to create mixed GPU/CPU field group"; + + if (result == RDC_ST_OK) { + // Clean up + rdc_group_field_destroy(rdc_handle_, field_group_id); + } +}