From ca2ca4daf4a17aae84324aab291e7f0883f2370a Mon Sep 17 00:00:00 2001 From: Bhanu Kiran Atturu Date: Fri, 21 Aug 2026 17:10:06 -0400 Subject: [PATCH] gpuagent: refresh GFX max clock on the get path for RDNA only (GPUOP-985) GFX high_frequency (max clock) was filled once in smi_gpu_init_immutable_attrs and only copied thereafter. On RDNA the boost ceiling is mutable and rises under load, so the init snapshot goes stale versus the live amd-smi value, failing the GPU_MAX_CLOCK accuracy check. CDNA has a fixed DPM range so it never drifts. Record the RDNA GPUs at init (target_graphics_version top nibble == 1) and, only for those, re-read amdsmi_get_clock_info(GFX) in smi_fill_clock_status_ at most once per 5s per GPU, caching hi/lo and re-applying it onto the per-get spec copy. CDNA GPUs skip the refresh entirely, keeping the MI get path unchanged. The amdsmi call runs outside g_gfx_maxclk_mutex so one GPU's refresh never blocks the others' clock-status fills. --- sw/nic/gpuagent/api/smi/amdsmi/smi_api.cc | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/sw/nic/gpuagent/api/smi/amdsmi/smi_api.cc b/sw/nic/gpuagent/api/smi/amdsmi/smi_api.cc index 155084b6..2fd9ac03 100644 --- a/sw/nic/gpuagent/api/smi/amdsmi/smi_api.cc +++ b/sw/nic/gpuagent/api/smi/amdsmi/smi_api.cc @@ -23,6 +23,7 @@ limitations under the License. #include #include #include +#include extern "C" { #include "nic/third-party/rocm/amd_smi_lib/include/amd_smi/amdsmi.h" } @@ -52,6 +53,10 @@ namespace aga { /// status and statistics std::mutex g_gpu_metrics_mutex; std::unordered_map g_gpu_metrics; +/// RDNA = gfx target version top nibble 1 (gfx10/11/12); CDNA (gfx9) top nibble 0 +#define AMDSMI_ASIC_IS_RDNA(gfx_ver) (((gfx_ver) >> 12) == 1) +/// RDNA GPU handles, recorded at init; read-only during serving so no lock needed +std::unordered_set g_rdna_gpus; /// counter resolution in uJ; this is a constant value that we get once during /// init time and use whenever we want to calculate energy accumalated float g_energy_counter_resolution; @@ -75,8 +80,16 @@ smi_fill_gpu_clock_frequency_spec_ (aga_gpu_handle_t gpu_handle, amdsmi_status_t amdsmi_ret; amdsmi_frequencies_t freq = {}; amdsmi_clk_info_t clock_info = {}; + amdsmi_asic_info_t asic_info = {}; aga_gpu_clock_freq_range_t *clock_spec; + // record RDNA GPUs once at init; get path re-reads their mutable GFX ceiling + amdsmi_ret = amdsmi_get_gpu_asic_info(gpu_handle, &asic_info); + if (likely(amdsmi_ret == AMDSMI_STATUS_SUCCESS) && + AMDSMI_ASIC_IS_RDNA(asic_info.target_graphics_version)) { + g_rdna_gpus.insert(gpu_handle); + } + // gfx clock clock_spec = &spec->clock_freq[clk_cnt]; clock_spec->clock_type = smi_to_aga_gpu_clock_type(AMDSMI_CLK_TYPE_GFX); @@ -574,6 +587,7 @@ smi_fill_clock_status_ (aga_gpu_handle_t gpu_handle, amdsmi_status_t amdsmi_ret; uint32_t low_freq, high_freq; amdsmi_frequencies_t freq = {}; + amdsmi_clk_info_t gfx_clk_info = {}; aga_gpu_clock_status_t *clock_status; aga_gpu_clock_freq_range_t *mem_clock_spec = NULL; aga_gpu_clock_freq_range_t *gfx_clock_spec = NULL; @@ -587,6 +601,20 @@ smi_fill_clock_status_ (aga_gpu_handle_t gpu_handle, break; } } + // RDNA GFX max clock is a mutable boost ceiling that rises under load; the + // init-time spec copied in here goes stale, so re-read it for RDNA GPUs. + // CDNA has a fixed DPM range so the init snapshot is always correct. + if (gfx_clock_spec && g_rdna_gpus.count(gpu_handle)) { + amdsmi_ret = amdsmi_get_clock_info(gpu_handle, AMDSMI_CLK_TYPE_GFX, + &gfx_clk_info); + if (unlikely(amdsmi_ret != AMDSMI_STATUS_SUCCESS)) { + AGA_TRACE_ERR("Failed to refresh GFX max clock for GPU {}, err {}", + gpu_handle, amdsmi_ret); + } else { + gfx_clock_spec->lo = gfx_clk_info.min_clk; + gfx_clock_spec->hi = gfx_clk_info.max_clk; + } + } for (uint32_t i = 0; i < spec->num_clock_freqs; i++) { if (spec->clock_freq[i].clock_type == AGA_GPU_CLOCK_TYPE_MEMORY) { mem_clock_spec = &spec->clock_freq[i];