Skip to main content

libamdgpu_top/
app_device_info.rs

1use crate::AMDGPU::{
2    ASIC_NAME,
3    CHIP_CLASS,
4    DeviceHandle,
5    drm_amdgpu_info_device,
6    drm_amdgpu_memory_info,
7    FW_VERSION::{FW_TYPE, FwVer},
8    GPU_INFO,
9    HW_IP::{HW_IP_TYPE, HwIpInfo},
10    HwId,
11    HwmonTemp,
12    IpDieEntry,
13    PowerCap,
14    PowerProfile,
15    RasBlock,
16    RasErrorCount,
17    VBIOS::VbiosInfo,
18    VIDEO_CAPS::{CAP_TYPE, VideoCapsInfo},
19};
20use crate::{DevicePath, get_hw_ip_info_list, PCI, stat::Sensors};
21use std::path::PathBuf;
22
23#[derive(Debug, Clone)]
24pub struct AppDeviceInfo {
25    pub ext_info: drm_amdgpu_info_device,
26    pub memory_info: drm_amdgpu_memory_info,
27    pub is_apu: bool,
28    pub resizable_bar: bool,
29    pub min_dpm_link: Option<PCI::LINK>,
30    pub max_dpm_link: Option<PCI::LINK>,
31    pub max_gpu_link: Option<PCI::LINK>,
32    pub max_system_link: Option<PCI::LINK>,
33    pub min_gpu_clk: u32,
34    pub max_gpu_clk: u32,
35    pub max_od_gpu_clk: Option<u32>,
36    pub min_mem_clk: u32,
37    pub max_mem_clk: u32,
38    pub max_od_mem_clk: Option<u32>,
39    pub marketing_name: String,
40    pub asic_name: ASIC_NAME,
41    pub pci_bus: PCI::BUS_INFO,
42    pub sysfs_path: PathBuf,
43    pub edge_temp: Option<HwmonTemp>,
44    pub junction_temp: Option<HwmonTemp>,
45    pub memory_temp: Option<HwmonTemp>,
46    pub power_cap: Option<PowerCap>,
47    pub fan_max_rpm: Option<u32>,
48    pub decode: Option<VideoCapsInfo>,
49    pub encode: Option<VideoCapsInfo>,
50    pub vbios: Option<VbiosInfo>,
51    pub l1_cache_size_kib_per_cu: u32,
52    pub actual_num_tcc_blocks: u32,
53    pub gl1_cache_size_kib_per_sa: u32,
54    pub total_l2_cache_size_kib: u32,
55    pub total_l3_cache_size_mib: u32,
56    pub hw_ip_info_list: Vec<HwIpInfo>,
57    pub ip_die_entries: Vec<IpDieEntry>,
58    pub power_profiles: Vec<PowerProfile>,
59    pub gfx_target_version: Option<String>,
60    pub ecc_memory: bool,
61    pub has_npu: bool,
62    pub smc_fw_version: Option<u32>,
63    pub smu_ip_version: Option<(u8, u8, u8)>, // MP0: APU, MP1: dGPU
64    pub fw_versions: Vec<FwVer>,
65    pub memory_vendor: Option<String>,
66    pub supports_gpu_metrics: bool,
67}
68
69impl AppDeviceInfo {
70    pub fn new(
71        amdgpu_dev: &DeviceHandle,
72        ext_info: &drm_amdgpu_info_device,
73        memory_info: &drm_amdgpu_memory_info,
74        sensors: &Option<Sensors>,
75        device_path: &DevicePath,
76    ) -> Self {
77        let (min_gpu_clk, max_gpu_clk) = amdgpu_dev.get_min_max_gpu_clock()
78            .unwrap_or_else(|| (0, (ext_info.max_engine_clock() / 1000) as u32));
79        let chip_class = ext_info.get_chip_class();
80        let (min_mem_clk, max_mem_clk) = amdgpu_dev.get_min_max_memory_clock()
81            .unwrap_or_else(|| (0, (ext_info.max_memory_clock() / 1000) as u32));
82        let resizable_bar = memory_info.check_resizable_bar();
83        let is_apu = ext_info.is_apu();
84        let marketing_name = device_path.device_name.clone();
85        let sysfs_path = device_path.sysfs_path.clone();
86        let hw_ip_info_list = get_hw_ip_info_list(amdgpu_dev, chip_class);
87        let ip_die_entries = IpDieEntry::get_all_entries_from_sysfs(&sysfs_path);
88        let power_profiles = PowerProfile::get_all_supported_profiles_from_sysfs(&sysfs_path);
89        let asic_name = ext_info.get_asic_name();
90        let gfx_target_version = ext_info.get_gfx_target_version().map(|v| v.to_string());
91
92        let max_od_gpu_clk;
93        let max_od_mem_clk;
94
95        {
96            let pp_od_clk_voltage = std::fs::read_to_string(sysfs_path.join("pp_od_clk_voltage"));
97
98            if is_apu {
99                max_od_gpu_clk = None;
100                max_od_mem_clk = None;
101            } else if chip_class == CHIP_CLASS::GFX12 && let Ok(s) = pp_od_clk_voltage {
102                // AMDGPU drivers do not expose reachable the boost clock of GFX12 to userspace,
103                // so we infer the boost clock from the sclk offset.
104                // https://gitlab.freedesktop.org/drm/amd/-/issues/4453
105                max_od_gpu_clk = Self::parse_od_sclk_offset(&s)
106                    .and_then(|[_min, max]| u32::try_from(max).ok())
107                    .map(|v| v + max_gpu_clk);
108                max_od_mem_clk = Self::parse_od_mclk(&s)
109                    .and_then(|[_min, max]| u32::try_from(max).ok());
110            } else if chip_class >= CHIP_CLASS::GFX10 && let Ok(s) = pp_od_clk_voltage {
111                max_od_gpu_clk = Self::parse_od_sclk(&s)
112                    .and_then(|[_min, max]| u32::try_from(max).ok());
113                max_od_mem_clk = Self::parse_od_mclk(&s)
114                    .and_then(|[_min, max]| u32::try_from(max).ok());
115            } else {
116                max_od_gpu_clk = None;
117                max_od_mem_clk = None;
118            }
119        }
120
121        let ecc_memory = RasErrorCount::get_from_sysfs_with_ras_block(&sysfs_path, RasBlock::UMC).is_ok();
122        let has_npu = is_apu && match asic_name {
123            ASIC_NAME::CHIP_GFX1103_R1 |
124            ASIC_NAME::CHIP_GFX1103_R1X => true,
125            _ => asic_name >= ASIC_NAME::CHIP_GFX1150,
126        };
127        let fw_versions = Self::get_fw_versions(amdgpu_dev);
128        let smc_fw_version = fw_versions
129            .iter()
130            .find(|fw_ver| fw_ver.fw_type == FW_TYPE::SMC)
131            .map(|fw_ver| fw_ver.version);
132        let smu_ip_version = ip_die_entries
133            .first()
134            .map(|entry| &entry.ip_hw_ids)
135            .and_then(|ip_hw_ids|
136                ip_hw_ids.iter().find(|ip| ip.hw_id == HwId::MP0 || ip.hw_id == HwId::MP1)
137            )
138            .and_then(|ip_hw_id| ip_hw_id.instances.first())
139            .map(|smu_ip| smu_ip.version());
140        let memory_vendor = std::fs::read_to_string(sysfs_path.join("mem_info_vram_vendor"))
141            .ok()
142            .map(|mut s| {
143                s.pop();
144                s
145            });
146        let supports_gpu_metrics = sysfs_path.join("gpu_metrics").exists();
147
148        Self {
149            ext_info: *ext_info,
150            memory_info: *memory_info,
151            resizable_bar,
152            is_apu,
153            min_dpm_link: sensors.as_ref().and_then(|s| s.min_dpm_link),
154            max_dpm_link: sensors.as_ref().and_then(|s| s.max_dpm_link),
155            max_gpu_link: sensors.as_ref().and_then(|s| s.max_gpu_link),
156            max_system_link: sensors.as_ref().and_then(|s| s.max_system_link),
157            min_gpu_clk,
158            max_gpu_clk,
159            max_od_gpu_clk,
160            min_mem_clk,
161            max_mem_clk,
162            max_od_mem_clk,
163            marketing_name,
164            asic_name,
165            pci_bus: device_path.pci,
166            sysfs_path,
167            edge_temp: sensors.as_ref().and_then(|s| s.edge_temp.clone()),
168            junction_temp: sensors.as_ref().and_then(|s| s.junction_temp.clone()),
169            memory_temp: sensors.as_ref().and_then(|s| s.memory_temp.clone()),
170            power_cap: sensors.as_ref().and_then(|s| s.power_cap.clone()),
171            fan_max_rpm: sensors.as_ref().and_then(|s| s.fan_max_rpm),
172            decode: amdgpu_dev.get_video_caps_info(CAP_TYPE::DECODE).ok(),
173            encode: amdgpu_dev.get_video_caps_info(CAP_TYPE::ENCODE).ok(),
174            vbios: amdgpu_dev.get_vbios_info().ok(),
175            actual_num_tcc_blocks: ext_info.get_actual_num_tcc_blocks(),
176            l1_cache_size_kib_per_cu: ext_info.get_l1_cache_size() >> 10,
177            gl1_cache_size_kib_per_sa: ext_info.get_gl1_cache_size() >> 10,
178            total_l2_cache_size_kib: ext_info.calc_l2_cache_size() >> 10,
179            total_l3_cache_size_mib: ext_info.calc_l3_cache_size_mb(),
180            hw_ip_info_list,
181            ip_die_entries,
182            power_profiles,
183            gfx_target_version,
184            ecc_memory,
185            has_npu,
186            smc_fw_version,
187            smu_ip_version,
188            fw_versions,
189            memory_vendor,
190            supports_gpu_metrics,
191        }
192    }
193
194    fn parse_mhz(s: &str) -> Option<i32> {
195        let len = s.len();
196        s.get(..len-3)?.parse::<i32>().ok()
197    }
198
199    fn parse_range(s: &str, start_str: &str) -> Option<[i32; 2]> {
200        let mut lines = s.lines();
201        let s_range = lines.find(|l| l.starts_with(start_str))?;
202        let mut split = s_range
203            .trim_start_matches(start_str)
204            .split_whitespace();
205
206        if let [Some(min), Some(max)] = [split.next(), split.next()]
207            .map(|v| v.and_then(Self::parse_mhz))
208        {
209            Some([min, max])
210        } else {
211            None
212        }
213    }
214
215    fn parse_od_sclk(s: &str) -> Option<[i32; 2]> {
216        Self::parse_range(s, "SCLK:")
217    }
218
219    fn parse_od_sclk_offset(s: &str) -> Option<[i32; 2]> {
220        Self::parse_range(s, "SCLK_OFFSET:")
221    }
222
223    fn parse_od_mclk(s: &str) -> Option<[i32; 2]> {
224        Self::parse_range(s, "MCLK:")
225    }
226
227    pub fn get_fw_versions(amdgpu_dev: &DeviceHandle) -> Vec<FwVer> {
228        const FW_LIST: &[FW_TYPE] = &[
229            FW_TYPE::VCE,
230            FW_TYPE::UVD,
231            FW_TYPE::GMC,
232            FW_TYPE::GFX_ME,
233            FW_TYPE::GFX_PFP,
234            FW_TYPE::GFX_CE,
235            FW_TYPE::GFX_RLC,
236            FW_TYPE::GFX_MEC,
237            FW_TYPE::SMC,
238            FW_TYPE::SDMA,
239            FW_TYPE::SOS,
240            FW_TYPE::ASD,
241            FW_TYPE::VCN,
242            FW_TYPE::GFX_RLC_RESTORE_LIST_CNTL,
243            FW_TYPE::GFX_RLC_RESTORE_LIST_GPM_MEM,
244            FW_TYPE::GFX_RLC_RESTORE_LIST_SRM_MEM,
245            FW_TYPE::DMCU,
246            FW_TYPE::TA,
247            FW_TYPE::DMCUB,
248            FW_TYPE::TOC,
249        ];
250
251        let mut fw_versions = Vec::with_capacity(24);
252
253        for fw_type in FW_LIST {
254            let fw_info = match amdgpu_dev.query_firmware_version(*fw_type, 0, 0) {
255                Ok(v) => v,
256                Err(_) => continue,
257            };
258            fw_versions.push(fw_info);
259        }
260
261        /* MEC2 */
262        if let Ok(mec2) = amdgpu_dev.query_firmware_version(FW_TYPE::GFX_MEC, 0, 1) {
263            fw_versions.push(mec2);
264        }
265
266        fw_versions
267    }
268
269    pub fn menu_entry(&self) -> String {
270        format!("{} ({})", self.marketing_name, self.pci_bus)
271    }
272
273    pub fn has_vcn(&self) -> bool {
274        self.hw_ip_info_list.iter().any(|info| info.ip_type == HW_IP_TYPE::VCN_DEC)
275    }
276
277    pub fn has_vcn_unified(&self) -> bool {
278        let Some(vcn_enc) = self.hw_ip_info_list
279            .iter()
280            .find(|info| info.ip_type == HW_IP_TYPE::VCN_ENC) else { return false };
281
282        4 <= vcn_enc.info.hw_ip_version_major
283    }
284
285    pub fn has_vpe(&self) -> bool {
286        if self.hw_ip_info_list.iter().any(|info| info.ip_type == HW_IP_TYPE::VPE) {
287            return true;
288        }
289
290        let Some(ip_die) = self.ip_die_entries.first() else { return false };
291
292        ip_die.ip_hw_ids.iter().any(|i| i.hw_id == HwId::VPE)
293    }
294}