From c7f9cff2cbb6b8c50e2dea1ec365b2bc5905d743 Mon Sep 17 00:00:00 2001 From: "Bill(Shuzhou) Liu" Date: Mon, 13 Nov 2023 09:05:05 -0600 Subject: [PATCH] Add APIs for PM table and register table Read the PM table and register table as the name value pair. Change-Id: Ie44fe67a28af3341bd6beb90d809e90f280351ac [ROCm/amdsmi commit: ac1ba333714e3ab2095c7b8142d201a5932525bb] --- projects/amdsmi/CMakeLists.txt | 2 + projects/amdsmi/include/amd_smi/amdsmi.h | 93 +++++ .../rocm_smi/include/rocm_smi/rocm_smi.h | 92 +++++ .../include/rocm_smi/rocm_smi_binary_parser.h | 262 ++++++++++++++ .../include/rocm_smi/rocm_smi_device.h | 3 + projects/amdsmi/rocm_smi/src/rocm_smi.cc | 42 +++ .../rocm_smi/src/rocm_smi_binary_parser.cc | 320 ++++++++++++++++++ .../amdsmi/rocm_smi/src/rocm_smi_device.cc | 14 + projects/amdsmi/rocm_smi/src/rocm_smi_main.cc | 2 + projects/amdsmi/src/amd_smi/amd_smi.cc | 25 ++ 10 files changed, 855 insertions(+) create mode 100644 projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_binary_parser.h create mode 100755 projects/amdsmi/rocm_smi/src/rocm_smi_binary_parser.cc diff --git a/projects/amdsmi/CMakeLists.txt b/projects/amdsmi/CMakeLists.txt index 3501c721f9..f2048cd86a 100755 --- a/projects/amdsmi/CMakeLists.txt +++ b/projects/amdsmi/CMakeLists.txt @@ -141,6 +141,7 @@ set(CMN_SRC_LIST "${ROCM_SRC_DIR}/rocm_smi_counters.cc" "${ROCM_SRC_DIR}/rocm_smi_device.cc" "${ROCM_SRC_DIR}/rocm_smi_gpu_metrics.cc" + "${ROCM_SRC_DIR}/rocm_smi_binary_parser.cc" "${ROCM_SRC_DIR}/rocm_smi_io_link.cc" "${ROCM_SRC_DIR}/rocm_smi_kfd.cc" "${ROCM_SRC_DIR}/rocm_smi_main.cc" @@ -163,6 +164,7 @@ set(CMN_INC_LIST "${ROCM_INC_DIR}/rocm_smi_counters.h" "${ROCM_INC_DIR}/rocm_smi_device.h" "${ROCM_INC_DIR}/rocm_smi_gpu_metrics.h" + "${ROCM_INC_DIR}/rocm_smi_binary_parser.h" "${ROCM_INC_DIR}/rocm_smi_exception.h" "${ROCM_INC_DIR}/rocm_smi_io_link.h" "${ROCM_INC_DIR}/rocm_smi_kfd.h" diff --git a/projects/amdsmi/include/amd_smi/amdsmi.h b/projects/amdsmi/include/amd_smi/amdsmi.h index 7856d8cf47..1aaf34d729 100644 --- a/projects/amdsmi/include/amd_smi/amdsmi.h +++ b/projects/amdsmi/include/amd_smi/amdsmi.h @@ -1213,6 +1213,28 @@ typedef struct { /// @endcond } amdsmi_gpu_metrics_t; + +#define MAX_AMDSMI_NAME_LENGTH 64 + +/** + * @brief This structure holds the name value pairs + */ +typedef struct { + char name[MAX_AMDSMI_NAME_LENGTH]; //!< Name + uint64_t value; //!< Use uint64_t to make it universal +} amdsmi_name_value_t; + +/** + * @brief This register type for register table + */ +typedef enum { + AMDSMI_REG_XGMI, + AMDSMI_REG_WAFL, + AMDSMI_REG_PCIE, + AMDSMI_REG_USR, + AMDSMI_REG_USR1, +} amdsmi_reg_type_t; + /** * @brief This structure holds ras feature */ @@ -2585,6 +2607,77 @@ amdsmi_status_t amdsmi_get_gpu_od_volt_info(amdsmi_processor_handle processor_ha amdsmi_status_t amdsmi_get_gpu_metrics_info(amdsmi_processor_handle processor_handle, amdsmi_gpu_metrics_t *pgpu_metrics); +/** + * @brief Get the pm metrics table with provided device index. + * + * @details Given a device handle @p processor_handle, @p pm_metrics pointer, + * and @p num_of_metrics pointer, + * this function will write the pm metrics name value pair + * to the array at @p pm_metrics and the number of metrics retreived to @p num_of_metrics + * Note: the library allocated memory for pm_metrics, and user must call + * free(pm_metrics) to free it after use. + * + * @param[in] processor_handle a processor handle + * + * @param[inout] pm_metrics A pointerto an array to hold multiple PM metrics. On successs, + * the library will allocate memory of pm_metrics and write metrics to this array. + * The caller must free this memory after usage to avoid memory leak. + * + * @param[inout] num_of_metrics a pointer to uint32_t to which the number of + * metrics is allocated for pm_metrics array as input, and the number of metrics retreived + * as output. If this parameter is NULL, this function will return + * ::AMDSMI_STATUS_INVALID_ARGS if the function is supported with the provided, + * arguments and ::AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the + * provided arguments. + * + * @retval ::AMDSMI_STATUS_SUCCESS call was successful + * @retval ::AMDSMI_STATUS_NOT_SUPPORTED installed software or hardware does not + * support this function with the given arguments + * @retval ::AMDSMI_STATUS_INVALID_ARGS the provided arguments are not valid + * + */ +amdsmi_status_t amdsmi_get_gpu_pm_metrics_info( + amdsmi_processor_handle processor_handle, + amdsmi_name_value_t** pm_metrics, + uint32_t *num_of_metrics); + +/** + * @brief Get the register metrics table with provided device index and register type. + * + * @details Given a device handle @p processor_handle, @p reg_type, @p reg_metrics pointer, + * and @p num_of_metrics pointer, + * this function will write the register metrics name value pair + * to the array at @p reg_metrics and the number of metrics retreived to @p num_of_metrics + * Note: the library allocated memory for reg_metrics, and user must call + * free(reg_metrics) to free it after use. + * + * @param[in] processor_handle a processor handle + * + * @param[in] reg_type The register type + * + * @param[inout] reg_metrics A pointerto an array to hold multiple register metrics. On successs, + * the library will allocate memory of reg_metrics and write metrics to this array. + * The caller must free this memory after usage to avoid memory leak. + * + * @param[inout] num_of_metrics a pointer to uint32_t to which the number of + * metrics is allocated for reg_metrics array as input, and the number of metrics retreived + * as output. If this parameter is NULL, this function will return + * ::AMDSMI_STATUS_INVALID_ARGS if the function is supported with the provided, + * arguments and ::AMDSMI_STATUS_NOT_SUPPORTED if it is not supported with the + * provided arguments. + * + * @retval ::AMDSMI_STATUS_SUCCESS call was successful + * @retval ::AMDSMI_STATUS_NOT_SUPPORTED installed software or hardware does not + * support this function with the given arguments + * @retval ::AMDSMI_STATUS_INVALID_ARGS the provided arguments are not valid + * + */ +amdsmi_status_t amdsmi_get_gpu_reg_table_info( + amdsmi_processor_handle processor_handle, + amdsmi_reg_type_t reg_type, + amdsmi_name_value_t** reg_metrics, + uint32_t *num_of_metrics); + /** * @brief This function sets the clock range information. It is not supported on virtual * machine guest diff --git a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h index 71de14939b..34a24223af 100755 --- a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h +++ b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi.h @@ -1063,6 +1063,28 @@ typedef struct { /// \endcond } rsmi_gpu_metrics_t; + +#define MAX_RSMI_NAME_LENGTH 64 + +/** + * @brief This structure holds the name value pairs + */ +typedef struct { + char name[MAX_RSMI_NAME_LENGTH]; //!< Name + uint64_t value; //!< Use uint64_t to make it universal +} rsmi_name_value_t; + +/** + * @brief This register type for register table + */ +typedef enum { + RSMI_REG_XGMI, + RSMI_REG_WAFL, + RSMI_REG_PCIE, + RSMI_REG_USR, + RSMI_REG_USR1, +} rsmi_reg_type_t; + /** * @brief This structure holds error counts. */ @@ -2765,6 +2787,76 @@ rsmi_status_t rsmi_dev_od_volt_info_get(uint32_t dv_ind, rsmi_status_t rsmi_dev_gpu_metrics_info_get(uint32_t dv_ind, rsmi_gpu_metrics_t *pgpu_metrics); +/** + * @brief Get the pm metrics table with provided device index. + * + * @details Given a device index @p dv_ind, @p pm_metrics pointer, + * and @p num_of_metrics pointer, + * this function will write the pm metrics name value pair + * to the array at @p pm_metrics and the number of metrics retreived to @p num_of_metrics + * Note: the library allocated memory for pm_metrics, and user must call + * free(pm_metrics) to free it after use. + * + * @param[in] dv_ind a device index + * + * @param[inout] pm_metrics A pointerto an array to hold multiple PM metrics. On successs, + * the library will allocate memory of pm_metrics and write metrics to this array. + * The caller must free this memory after usage to avoid memory leak. + * + * @param[inout] num_of_metrics a pointer to uint32_t to which the number of + * metrics is allocated for pm_metrics array as input, and the number of metrics retreived + * as output. If this parameter is NULL, this function will return + * ::RSMI_STATUS_INVALID_ARGS if the function is supported with the provided, + * arguments and ::RSMI_STATUS_NOT_SUPPORTED if it is not supported with the + * provided arguments. + * + * @retval ::RSMI_STATUS_SUCCESS call was successful + * @retval ::RSMI_STATUS_NOT_SUPPORTED installed software or hardware does not + * support this function with the given arguments + * @retval ::RSMI_STATUS_INVALID_ARGS the provided arguments are not valid + * + */ +rsmi_status_t rsmi_dev_pm_metrics_info_get(uint32_t dv_ind, + rsmi_name_value_t** pm_metrics, + uint32_t *num_of_metrics); + + +/** + * @brief Get the register metrics table with provided device index and registertype. + * + * @details Given a device index @p dv_ind, @p reg_type, @p reg_metrics pointer, + * and @p num_of_metrics pointer, + * this function will write the register metrics name value pair + * to the array at @p reg_metrics and the number of metrics retreived to @p num_of_metrics + * Note: the library allocated memory for reg_metrics, and user must call + * free(reg_metrics) to free it after use. + * + * @param[in] dv_ind a device index + * + * @param[in] reg_type The register type + * + * @param[inout] reg_metrics A pointerto an array to hold multiple register metrics. On successs, + * the library will allocate memory of reg_metrics and write metrics to this array. + * The caller must free this memory after usage to avoid memory leak. + * + * @param[inout] num_of_metrics a pointer to uint32_t to which the number of + * metrics is allocated for reg_metrics array as input, and the number of metrics retreived + * as output. If this parameter is NULL, this function will return + * ::RSMI_STATUS_INVALID_ARGS if the function is supported with the provided, + * arguments and ::RSMI_STATUS_NOT_SUPPORTED if it is not supported with the + * provided arguments. + * + * @retval ::RSMI_STATUS_SUCCESS call was successful + * @retval ::RSMI_STATUS_NOT_SUPPORTED installed software or hardware does not + * support this function with the given arguments + * @retval ::RSMI_STATUS_INVALID_ARGS the provided arguments are not valid + * + */ +rsmi_status_t rsmi_dev_reg_table_info_get(uint32_t dv_ind, + rsmi_reg_type_t reg_type, + rsmi_name_value_t** reg_metrics, + uint32_t *num_of_metrics); + /** * @brief This function sets the clock range information * diff --git a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_binary_parser.h b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_binary_parser.h new file mode 100644 index 0000000000..6c48f5e7b5 --- /dev/null +++ b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_binary_parser.h @@ -0,0 +1,262 @@ +/* + * ============================================================================= + * The University of Illinois/NCSA + * Open Source License (NCSA) + * + * Copyright (c) 2017-2023, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Developed by: + * + * AMD Research and AMD ROC Software Development + * + * Advanced Micro Devices, Inc. + * + * www.amd.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal with 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: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimers. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in + * the documentation and/or other materials provided with the distribution. + * - Neither the names of , + * nor the names of its contributors may be used to endorse or promote + * products derived from this Software without specific prior written + * permission. + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#ifndef ROCM_SMI_ROCM_SMI_BINARY_PARSER_H_ +#define ROCM_SMI_ROCM_SMI_BINARY_PARSER_H_ + +#include "rocm_smi/rocm_smi_common.h" +#include "rocm_smi/rocm_smi.h" + +#include +#include +#include +#include +#include + +/** + * A binary parser to read the pm table and register table + * + */ +namespace amd::smi { + +// definition for register table +enum amdgpu_sysfs_reg_offset { + AMDGPU_SYS_REG_STATE_XGMI = 0x0000, + AMDGPU_SYS_REG_STATE_WAFL = 0x1000, + AMDGPU_SYS_REG_STATE_PCIE = 0x2000, + AMDGPU_SYS_REG_STATE_USR = 0x3000, + AMDGPU_SYS_REG_STATE_USR_1 = 0x4000, + AMDGPU_SYS_REG_STATE_END = 0x5000, +}; + +#define FIELD_FLAG_NUM_INSTANCE 0x01 +#define FIELD_FLAG_NUM_SMN 0x02 +#define FIELD_FLAG_INSTANCE_START 0x04 +#define FIELD_FLAG_SMN_START 0x08 + +#define FIELD_TYPE_U8 0x01 +#define FIELD_TYPE_U16 0x02 +#define FIELD_TYPE_U32 0x04 +#define FIELD_TYPE_U64 0x08 + +struct metric_field { + uint8_t field_type; + int field_arr_size; + const char *field_name; + uint8_t field_flag; +}; + +struct metric_field xgmi_regs[] = { + { FIELD_TYPE_U16, 1, "structure_size", 0 }, + { FIELD_TYPE_U8, 1, "format_revision", 0 }, + { FIELD_TYPE_U8, 1, "content_revision", 0 }, + { FIELD_TYPE_U8, 1, "state_type", 0 }, + { FIELD_TYPE_U8, 1, "num_instances", FIELD_FLAG_NUM_INSTANCE }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U16, 1, "instance", FIELD_FLAG_INSTANCE_START }, + { FIELD_TYPE_U16, 1, "state", 0 }, + { FIELD_TYPE_U16, 1, "num_smn_regs", FIELD_FLAG_NUM_SMN }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U64, 1, "addr", FIELD_FLAG_SMN_START }, + { FIELD_TYPE_U32, 1, "value", 0 }, + { FIELD_TYPE_U32, 1, "pad", 0 }, + { 0, 0, NULL, 0 }, +}; + +// check me! +struct metric_field wafl_regs[] = { + { FIELD_TYPE_U16, 1, "structure_size", 0 }, + { FIELD_TYPE_U8, 1, "format_revision", 0 }, + { FIELD_TYPE_U8, 1, "content_revision", 0 }, + { FIELD_TYPE_U8, 1, "state_type", 0 }, + { FIELD_TYPE_U8, 1, "num_instances", FIELD_FLAG_NUM_INSTANCE }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U16, 1, "instance", FIELD_FLAG_INSTANCE_START }, + { FIELD_TYPE_U16, 1, "state", 0 }, + { FIELD_TYPE_U16, 1, "num_smn_regs", FIELD_FLAG_NUM_SMN }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U64, 1, "addr", FIELD_FLAG_SMN_START }, + { FIELD_TYPE_U32, 1, "value", 0 }, + { FIELD_TYPE_U32, 1, "pad", 0 }, + { 0, 0, NULL, 0 }, +}; + +struct metric_field pcie_regs[] = { + { FIELD_TYPE_U16, 1, "structure_size", 0 }, + { FIELD_TYPE_U8, 1, "format_revision", 0 }, + { FIELD_TYPE_U8, 1, "content_revision", 0 }, + { FIELD_TYPE_U8, 1, "state_type", 0 }, + { FIELD_TYPE_U8, 1, "num_instances", FIELD_FLAG_NUM_INSTANCE }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U16, 1, "instance", FIELD_FLAG_INSTANCE_START }, + { FIELD_TYPE_U16, 1, "state", 0 }, + { FIELD_TYPE_U16, 1, "num_smn_regs", FIELD_FLAG_NUM_SMN }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U16, 1, "device_status", 0 }, + { FIELD_TYPE_U16, 1, "link_status", 0 }, + { FIELD_TYPE_U32, 1, "sub_bus_number_latency", 0 }, + { FIELD_TYPE_U32, 1, "pcie_corr_err_status", 0 }, + { FIELD_TYPE_U32, 1, "pcie_uncorr_err_status", 0 }, + + { FIELD_TYPE_U64, 1, "addr", FIELD_FLAG_SMN_START }, + { FIELD_TYPE_U32, 1, "value", 0 }, + { FIELD_TYPE_U32, 1, "pad", 0 }, + { 0, 0, NULL, 0 }, +}; + +struct metric_field usr_regs[] = { + { FIELD_TYPE_U16, 1, "structure_size", 0 }, + { FIELD_TYPE_U8, 1, "format_revision", 0 }, + { FIELD_TYPE_U8, 1, "content_revision", 0 }, + { FIELD_TYPE_U8, 1, "state_type", 0 }, + { FIELD_TYPE_U8, 1, "num_instances", FIELD_FLAG_NUM_INSTANCE }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U16, 1, "instance", FIELD_FLAG_INSTANCE_START }, + { FIELD_TYPE_U16, 1, "state", 0 }, + { FIELD_TYPE_U16, 1, "num_smn_regs", FIELD_FLAG_NUM_SMN }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + + { FIELD_TYPE_U64, 1, "addr", FIELD_FLAG_SMN_START }, + { FIELD_TYPE_U32, 1, "value", 0 }, + { FIELD_TYPE_U32, 1, "pad", 0 }, + { 0, 0, NULL, 0 }, +}; + +// definition for pm metrics table +#define FIELD_FLAG_ACCUMULATOR 0x01 + +struct metric_field smu_13_0_6_v8[] = { + { FIELD_TYPE_U16, 1, "structure_size", 0 }, + { FIELD_TYPE_U16, 1, "pad", 0 }, + { FIELD_TYPE_U32, 1, "mp1_ip_discovery_version", 0 }, + { FIELD_TYPE_U32, 1, "pmfw_version", 0 }, + { FIELD_TYPE_U32, 1, "pmmetrics_version", 0 }, + + { FIELD_TYPE_U32, 1, "AccumulationCounter", 0 }, + + { FIELD_TYPE_U32, 1, "MaxSocketTemperature", 0 }, + { FIELD_TYPE_U32, 1, "MaxVrTemperature", 0 }, + { FIELD_TYPE_U32, 1, "MaxHbmTemperature", 0 }, + { FIELD_TYPE_U64, 1, "MaxSocketTemperatureAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "MaxVrTemperatureAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "MaxHbmTemperatureAcc", FIELD_FLAG_ACCUMULATOR }, + + { FIELD_TYPE_U32, 1, "SocketPowerLimit", 0 }, + { FIELD_TYPE_U32, 1, "MaxSocketPowerLimit", 0 }, + { FIELD_TYPE_U32, 1, "SocketPower", 0 }, + + { FIELD_TYPE_U64, 1, "Timestamp", 0 }, + { FIELD_TYPE_U64, 1, "SocketEnergyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "CcdEnergyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "XcdEnergyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "AidEnergyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "HbmEnergyAcc", FIELD_FLAG_ACCUMULATOR }, + + { FIELD_TYPE_U32, 1, "CclkFrequencyLimit", 0 }, + { FIELD_TYPE_U32, 1, "GfxclkFrequencyLimit", 0 }, + { FIELD_TYPE_U32, 1, "FclkFrequency", 0 }, + { FIELD_TYPE_U32, 1, "UclkFrequency", 0 }, + { FIELD_TYPE_U32, 4, "SocclkFrequency", 0 }, + { FIELD_TYPE_U32, 4, "VclkFrequency", 0 }, + { FIELD_TYPE_U32, 4, "DclkFrequency", 0 }, + { FIELD_TYPE_U32, 4, "LclkFrequency", 0 }, + { FIELD_TYPE_U64, 8, "GfxclkFrequencyAcc", FIELD_FLAG_ACCUMULATOR}, + { FIELD_TYPE_U64, 96, "CclkFrequencyAcc", FIELD_FLAG_ACCUMULATOR }, + + { FIELD_TYPE_U32, 1, "MaxCclkFrequency", 0 }, + { FIELD_TYPE_U32, 1, "MinCclkFrequency", 0 }, + { FIELD_TYPE_U32, 1, "MaxGfxclkFrequency", 0 }, + { FIELD_TYPE_U32, 1, "MinGfxclkFrequency", 0 }, + { FIELD_TYPE_U32, 4, "FclkFrequencyTable", 0 }, + { FIELD_TYPE_U32, 4, "UclkFrequencyTable", 0 }, + { FIELD_TYPE_U32, 4, "SocclkFrequencyTable", 0 }, + { FIELD_TYPE_U32, 4, "VclkFrequencyTable", 0 }, + { FIELD_TYPE_U32, 4, "DclkFrequencyTable", 0 }, + { FIELD_TYPE_U32, 4, "LclkFrequencyTable", 0 }, + { FIELD_TYPE_U32, 1, "MaxLclkDpmRange", 0 }, + { FIELD_TYPE_U32, 1, "MinLclkDpmRange", 0 }, + + { FIELD_TYPE_U32, 1, "XgmiWidth", 0 }, + { FIELD_TYPE_U32, 1, "XgmiBitrate", 0 }, + { FIELD_TYPE_U64, 8, "XgmiReadBandwidthAcc", 0 }, + { FIELD_TYPE_U64, 8, "XgmiWriteBandwidthAcc", 0 }, + + { FIELD_TYPE_U32, 1, "SocketC0Residency", 0 }, + { FIELD_TYPE_U32, 1, "SocketGfxBusy", 0 }, + { FIELD_TYPE_U32, 1, "DramBandwidthUtilization", 0 }, + { FIELD_TYPE_U64, 1, "SocketC0ResidencyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "SocketGfxBusyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 1, "DramBandwidthAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U32, 1, "MaxDramBandwidth", 0 }, + { FIELD_TYPE_U64, 1, "DramBandwidthUtilizationAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 4, "PcieBandwidthAcc", FIELD_FLAG_ACCUMULATOR }, + + { FIELD_TYPE_U32, 1, "ProchotResidencyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U32, 1, "PptResidencyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U32, 1, "SocketThmResidencyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U32, 1, "VrThmResidencyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U32, 1, "HbmThmResidencyAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U32, 1, "GfxLockXCDMak", 0 }, + + { FIELD_TYPE_U32, 8, "GfxclkFrequency", 0 }, + + { FIELD_TYPE_U64, 4, "PublicSerialNumber_AID", 0 }, + { FIELD_TYPE_U64, 8, "PublicSerialNumber_XCD", 0 }, + { FIELD_TYPE_U64, 12, "PublicSerialNumber_CCD", 0 }, + + { FIELD_TYPE_U64, 8, "XgmiReadDataSizeAcc", FIELD_FLAG_ACCUMULATOR }, + { FIELD_TYPE_U64, 8, "XgmiWriteDataSizeAcc", FIELD_FLAG_ACCUMULATOR }, + { 0, 0, NULL, 0 }, +}; + +} // namespace amd::smi + +#endif // ROCM_SMI_ROCM_SMI_BINARY_PARSER_H_ + diff --git a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_device.h b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_device.h index a64852133a..1e6d2f6f34 100755 --- a/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_device.h +++ b/projects/amdsmi/rocm_smi/include/rocm_smi/rocm_smi_device.h @@ -171,6 +171,8 @@ enum DevInfoTypes { kDevMemPageBad, kDevNumaNode, kDevGpuMetrics, + kDevPmMetrics, + kDevRegMetrics, kDevGpuReset, kDevAvailableComputePartition, kDevComputePartition, @@ -205,6 +207,7 @@ class Device { int readDevInfo(DevInfoTypes type, std::vector *retVec); int readDevInfo(DevInfoTypes type, std::size_t b_size, void *p_binary_data); + std::string get_sys_file_path_by_type(DevInfoTypes type) const; // Get the property from a file which may contain multiple properties. int readDevInfo(DevInfoTypes type, const std::string& property, std::string& value); diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi.cc b/projects/amdsmi/rocm_smi/src/rocm_smi.cc index d9251397e3..99cea1795b 100755 --- a/projects/amdsmi/rocm_smi/src/rocm_smi.cc +++ b/projects/amdsmi/rocm_smi/src/rocm_smi.cc @@ -106,6 +106,14 @@ static const std::map kClkTypeMap = { #define TRY try { #define CATCH } catch (...) {return amd::smi::handleException();} +// declare pm metrics and register table function +namespace amd::smi { +int present_pmmetrics(const char* sysfs_file_name, + rsmi_name_value_t **kv, uint32_t *kvnum); +int present_reg_state(const char* fname, rsmi_reg_type_t reg_type, + rsmi_name_value_t **kv, uint32_t *kvnum); +} + static uint64_t get_multiplier_from_str(char units_char) { uint32_t multiplier = 0; @@ -2479,6 +2487,40 @@ rsmi_dev_vendor_name_get(uint32_t dv_ind, char *name, size_t len) { CATCH } +rsmi_status_t rsmi_dev_pm_metrics_info_get(uint32_t dv_ind, + rsmi_name_value_t** pm_metrics, + uint32_t *num_of_metrics) { + TRY + DEVICE_MUTEX + CHK_SUPPORT_NAME_ONLY(num_of_metrics) + std::string file_path = dev-> + get_sys_file_path_by_type(amd::smi::kDevPmMetrics); + + int ret = amd::smi::present_pmmetrics( + file_path.c_str(), pm_metrics, num_of_metrics); + if (ret == 0) return RSMI_STATUS_SUCCESS; + return RSMI_STATUS_NOT_SUPPORTED; + + CATCH +} + +rsmi_status_t rsmi_dev_reg_table_info_get(uint32_t dv_ind, + rsmi_reg_type_t reg_type, + rsmi_name_value_t** reg_metrics, + uint32_t *num_of_metrics) { + TRY + DEVICE_MUTEX + CHK_SUPPORT_NAME_ONLY(num_of_metrics) + std::string file_path = dev-> + get_sys_file_path_by_type(amd::smi::kDevRegMetrics); + + int ret = amd::smi::present_reg_state( + file_path.c_str(), reg_type, reg_metrics, num_of_metrics); + if (ret == 0) return RSMI_STATUS_SUCCESS; + return RSMI_STATUS_NOT_SUPPORTED; + + CATCH +} rsmi_status_t rsmi_dev_pci_bandwidth_get(uint32_t dv_ind, rsmi_pcie_bandwidth_t *b) { diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi_binary_parser.cc b/projects/amdsmi/rocm_smi/src/rocm_smi_binary_parser.cc new file mode 100755 index 0000000000..805be841c2 --- /dev/null +++ b/projects/amdsmi/rocm_smi/src/rocm_smi_binary_parser.cc @@ -0,0 +1,320 @@ +/* + * ============================================================================= + * The University of Illinois/NCSA + * Open Source License (NCSA) + * + * Copyright (c) 2017-2023, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Developed by: + * + * AMD Research and AMD ROC Software Development + * + * Advanced Micro Devices, Inc. + * + * www.amd.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal with 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: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimers. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimers in + * the documentation and/or other materials provided with the distribution. + * - Neither the names of , + * nor the names of its contributors may be used to endorse or promote + * products derived from this Software without specific prior written + * permission. + * + * 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 CONTRIBUTORS 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 WITH THE SOFTWARE. + * + */ + +#include "rocm_smi/rocm_smi_binary_parser.h" +#include "rocm_smi/rocm_smi_common.h" // Should go before rocm_smi.h +#include "rocm_smi/rocm_smi.h" +#include "rocm_smi/rocm_smi_main.h" +#include "rocm_smi/rocm_smi_utils.h" +#include "rocm_smi/rocm_smi_exception.h" +#include "rocm_smi/rocm_smi_device.h" +#include "rocm_smi/rocm_smi_logger.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace amd::smi { +static uint64_t get_value(uint8_t **ptr, struct metric_field *field) { + uint64_t v; + switch (field->field_type) { + case FIELD_TYPE_U8: + v = *(uint8_t*)(*ptr); + ++(*ptr); + break; + case FIELD_TYPE_U16: + v = *(uint16_t*)(*ptr); + (*ptr) += 2; + break; + case FIELD_TYPE_U32: + v = *(uint32_t*)(*ptr); + (*ptr) += 4; + break; + case FIELD_TYPE_U64: + v = *(uint64_t*)(*ptr); + (*ptr) += 8; + break; + } + return v; +} + +static int parse_pmmetric_table(uint8_t *buf, struct metric_field *table, + int32_t buflen, rsmi_name_value_t **kv, uint32_t *kvnum) { + uint64_t v1; + int x, y; + uint8_t *origbuf = buf; + uint32_t kvsize = 64; + + *kv = reinterpret_cast(calloc(kvsize, sizeof **kv)); + *kvnum = 0; + + for (x = 0; table[x].field_name; x++) { + for (y = 0; y < table[x].field_arr_size; y++) { + v1 = get_value(&buf, &table[x]); + if ((intptr_t)(buf - origbuf) > buflen) { + fprintf(stderr, + "[ERROR]: Invalid buffer as buffer length exceeded\n"); + return -1; + } + + if (*kvnum == kvsize) { + kvsize += 64; + *kv = reinterpret_cast + (realloc(*kv,kvsize * (sizeof **kv))); + } + if (table[x].field_arr_size == 1) { + sprintf((*kv)[*kvnum].name, "%s", table[x].field_name); + } else { + sprintf((*kv)[*kvnum].name, "%s[%d]", table[x].field_name, y); + } + (*kv)[*kvnum].value = v1; + + ++(*kvnum); + } + } + return 0; +} + +/** present the PM metrics data + * + * @dri_instance: which card to pick under /sys/class/drm/card${instance}/device/ + * @kv: pointer to pointer of rsmi_name_value pairs + * @kvnum: pointer to number of used rsmi_name_value pairs + */ +int present_pmmetrics(const char* fname, + rsmi_name_value_t **kv, uint32_t *kvnum) +{ + uint8_t *buf1; + FILE *infile; + uint32_t pmmetrics_version; + metric_field *table; + int r; + int32_t len; + + infile = fopen(fname, "rb"); + if (!infile) { + fprintf(stderr, "[ERROR]: pm_metrics file not found \n"); + return -1; + } + + buf1 = reinterpret_cast(calloc(1, 65536)); + if (!buf1) { + return -1; + } + + table = NULL; + len = fread(buf1, 1, 65536, infile); + fseek(infile, 0, SEEK_SET); + memcpy(&pmmetrics_version, &buf1[12], 4); + + switch (pmmetrics_version) { + case 4: // ??? why 4? + table = &smu_13_0_6_v8[0]; + break; + default: + fprintf(stderr, "Metrics version %d not supported\n" + , pmmetrics_version); + return -1; + } + r = parse_pmmetric_table(buf1, table, len, kv, kvnum); + fclose(infile); + free(buf1); + return r; +} + +static int parse_reg_state_table(uint8_t *buf, int32_t buflen, + struct metric_field *table, + rsmi_name_value_t **kv, uint32_t *kvnum) { + int skip_smn, x, y, cur_instance, cur_smn, + num_instance, num_smn, instance_start, smn_start; + uint64_t v; + uint8_t *obuf, *origbuf; + int kvsize = 64; + + *kv = reinterpret_cast(calloc(kvsize, sizeof **kv)); + *kvnum = 0; + + skip_smn = cur_instance = num_instance = num_smn = 0; + instance_start = smn_start = 0x1000; + x = 0; + origbuf = buf; +top: + while (table[x].field_name != NULL) { + for (y = 0; y < table[x].field_arr_size; y++) { + obuf = buf; + v = get_value(&buf, &table[x]); + if ((intptr_t)(buf - origbuf) > buflen) { + fprintf(stderr, + "[ERROR] Invalid buffer as read length was exceeded\n"); + return -1; + } + switch (table[x].field_flag) { + case FIELD_FLAG_INSTANCE_START: + instance_start = x; + num_smn = cur_smn = 0; + break; + case FIELD_FLAG_SMN_START: + // if we hit an SMN start but there are no registers then skip back to the start + // of the instance block + if (skip_smn) { + // out of instances we're done so bail! + if (!num_instance) + return 0; + x = instance_start; + --num_instance; + ++cur_instance; + // rewind the buffer since we didn't actually consume + // this word + buf = obuf; + goto top; + } else { + smn_start = x; + } + break; + case FIELD_FLAG_NUM_INSTANCE: + num_instance = v; + break; + case FIELD_FLAG_NUM_SMN: + num_smn = v; + if (v) + skip_smn = 0; + else + skip_smn = 1; + break; + } + if (*kvnum == kvsize) { + kvsize += 64; + *kv = reinterpret_cast( + realloc(*kv, kvsize * (sizeof **kv))); + } + sprintf((*kv)[*kvnum].name, "%s", table[x].field_name); + if (table[x].field_arr_size > 1) { + sprintf((*kv)[*kvnum].name + strlen((*kv)[*kvnum].name), "[%d]", y); + } + if (x >= instance_start) + sprintf((*kv)[*kvnum].name + strlen((*kv)[*kvnum].name), ".instance[%d]", cur_instance); + if (x >= smn_start) + sprintf((*kv)[*kvnum].name + strlen((*kv)[*kvnum].name), ".smn[%d]", cur_smn); + (*kv)[*kvnum].value = v; + ++(*kvnum); + } + + // done move to next or loop + ++x; + if (table[x].field_name == NULL && --num_smn) { + x = smn_start; + ++cur_smn; + } else if (table[x].field_name == NULL && --num_instance) { + x = instance_start; + ++cur_instance; + } + } + return 0; +} + +/** present_reg_state: present register state data + * + * @dri_instance: The DRI instance to select under /sys/class/drm/card${instance}/device + * @table: Name of the table to read {"xgmi', "wafl", "pcie", "usr", "usr_1"} + * @kv: pointer to pointer of rsmi_name_value pairs + * @kvnum: pointer to number of used rsmi_name_value pairs + */ +int present_reg_state(const char* fname, + rsmi_reg_type_t reg_type, rsmi_name_value_t **kv, uint32_t *kvnum) { + uint8_t buf[4096]; + FILE *infile; + struct metric_field *tab; + int32_t len; + + infile = fopen(fname, "rb"); + if (!infile) { + fprintf(stderr, "[ERROR]: reg_state file not found\n"); + return -1; + } + + tab = NULL; + if (reg_type == RSMI_REG_XGMI) { + fseek(infile, AMDGPU_SYS_REG_STATE_XGMI, SEEK_SET); + tab = &xgmi_regs[0]; + } + if (reg_type == RSMI_REG_WAFL) { + fseek(infile, AMDGPU_SYS_REG_STATE_WAFL, SEEK_SET); + tab = &wafl_regs[0]; + } + if (reg_type == RSMI_REG_PCIE) { + fseek(infile, AMDGPU_SYS_REG_STATE_PCIE, SEEK_SET); + tab = &pcie_regs[0]; + } + if (reg_type == RSMI_REG_USR) { + fseek(infile, AMDGPU_SYS_REG_STATE_USR, SEEK_SET); + tab = &usr_regs[0]; + } + if (reg_type == RSMI_REG_USR1) { + fseek(infile, AMDGPU_SYS_REG_STATE_USR_1, SEEK_SET); + tab = &usr_regs[0]; + } + if (!tab) { + fprintf(stderr, "[ERROR]: Invalid register space named <%d>\n", reg_type); + fclose(infile); + return -2; + } + + len = fread(buf, 1, sizeof buf, infile); + fclose(infile); + return parse_reg_state_table(buf, len, tab, kv, kvnum); +} + + +} // namespace amd::smi diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi_device.cc b/projects/amdsmi/rocm_smi/src/rocm_smi_device.cc index dc0506a2ab..4ad7860319 100755 --- a/projects/amdsmi/rocm_smi/src/rocm_smi_device.cc +++ b/projects/amdsmi/rocm_smi/src/rocm_smi_device.cc @@ -130,6 +130,8 @@ static const char *kDevXGMIErrorFName = "xgmi_error"; static const char *kDevSerialNumberFName = "serial_number"; static const char *kDevNumaNodeFName = "numa_node"; static const char *kDevGpuMetricsFName = "gpu_metrics"; +static const char *kDevPmMetricsFName = "pm_metrics"; // PM log +static const char *kDevRegMetricsFName = "reg_state"; // register table static const char *kDevAvailableComputePartitionFName = "available_compute_partition"; static const char *kDevComputePartitionFName = "current_compute_partition"; @@ -312,6 +314,8 @@ static const std::map kDevAttribNameMap = { {kDevMemPageBad, kDevMemPageBadFName}, {kDevNumaNode, kDevNumaNodeFName}, {kDevGpuMetrics, kDevGpuMetricsFName}, + {kDevPmMetrics, kDevPmMetricsFName}, + {kDevRegMetrics, kDevRegMetricsFName}, {kDevGpuReset, kDevGpuResetFName}, {kDevAvailableComputePartition, kDevAvailableComputePartitionFName}, {kDevComputePartition, kDevComputePartitionFName}, @@ -445,6 +449,8 @@ static const std::map kDevFuncDependsMap = { {"rsmi_dev_memory_reserved_pages_get", {{kDevMemPageBadFName}, {}}}, {"rsmi_topo_numa_affinity_get", {{kDevNumaNodeFName}, {}}}, {"rsmi_dev_gpu_metrics_info_get", {{kDevGpuMetricsFName}, {}}}, + {"rsmi_dev_pm_metrics_info_get", {{kDevPmMetricsFName}, {}}}, + {"rsmi_dev_reg_table_info_get", {{kDevRegMetricsFName}, {}}}, {"rsmi_dev_gpu_reset", {{kDevGpuResetFName}, {}}}, {"rsmi_dev_compute_partition_get", {{kDevComputePartitionFName}, {}}}, {"rsmi_dev_compute_partition_set", {{kDevComputePartitionFName}, {}}}, @@ -594,6 +600,14 @@ int Device::openDebugFileStream(DevInfoTypes type, T *fs, const char *str) { return 0; } +std::string Device::get_sys_file_path_by_type(DevInfoTypes type) const { + auto sysfs_path = path_; + sysfs_path += "/device/"; + sysfs_path += kDevAttribNameMap.at(type); + + return sysfs_path; +} + template int Device::openSysfsFileStream(DevInfoTypes type, T *fs, const char *str) { auto sysfs_path = path_; diff --git a/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc b/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc index 98fae2a663..7365fe9cfc 100755 --- a/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc +++ b/projects/amdsmi/rocm_smi/src/rocm_smi_main.cc @@ -154,6 +154,8 @@ amd::smi::RocmSMI::devInfoTypesStrings = { {amd::smi::kDevMemPageBad, amdSMI + "kDevMemPageBad"}, {amd::smi::kDevNumaNode, amdSMI + "kDevNumaNode"}, {amd::smi::kDevGpuMetrics, amdSMI + "kDevGpuMetrics"}, + {amd::smi::kDevPmMetrics, amdSMI + "kDevPmMetrics"}, + {amd::smi::kDevRegMetrics, amdSMI + "kDevRegMetrics"}, {amd::smi::kDevGpuReset, amdSMI + "kDevGpuReset"}, {amd::smi::kDevAvailableComputePartition, amdSMI + "kDevAvailableComputePartition"}, diff --git a/projects/amdsmi/src/amd_smi/amd_smi.cc b/projects/amdsmi/src/amd_smi/amd_smi.cc index 1153416c66..4415783f93 100644 --- a/projects/amdsmi/src/amd_smi/amd_smi.cc +++ b/projects/amdsmi/src/amd_smi/amd_smi.cc @@ -1157,6 +1157,31 @@ amdsmi_status_t amdsmi_get_gpu_metrics_info( reinterpret_cast(pgpu_metrics)); } + +amdsmi_status_t amdsmi_get_gpu_pm_metrics_info( + amdsmi_processor_handle processor_handle, + amdsmi_name_value_t** pm_metrics, + uint32_t *num_of_metrics) { + AMDSMI_CHECK_INIT(); + + return rsmi_wrapper(rsmi_dev_pm_metrics_info_get, processor_handle, + reinterpret_cast(pm_metrics), + num_of_metrics); +} + +amdsmi_status_t amdsmi_get_gpu_reg_table_info( + amdsmi_processor_handle processor_handle, + amdsmi_reg_type_t reg_type, + amdsmi_name_value_t** reg_metrics, + uint32_t *num_of_metrics) { + AMDSMI_CHECK_INIT(); + + return rsmi_wrapper(rsmi_dev_reg_table_info_get, processor_handle, + static_cast(reg_type), + reinterpret_cast(reg_metrics), + num_of_metrics); +} + amdsmi_status_t amdsmi_get_power_cap_info(amdsmi_processor_handle processor_handle, uint32_t sensor_ind,