Skip to main content

libdrm_amdgpu_sys/amdgpu/
device_handle.rs

1use crate::AMDGPU::DEVICE_HANDLE;
2use crate::*;
3#[cfg(feature = "dynamic_loading")]
4use std::sync::Arc;
5#[cfg(feature = "dynamic_loading")]
6use bindings::{DynLibDrm, DynLibDrmAmdgpu};
7
8use crate::bindings::drmDevicePtr;
9pub use bindings::{
10    amdgpu_device_handle,
11    // amdgpu_device_initialize,
12    amdgpu_gds_resource_info,
13    amdgpu_gpu_info,
14    drm_amdgpu_heap_info,
15    drm_amdgpu_info_device,
16    drm_amdgpu_info_gds,
17    drm_amdgpu_info_vram_gtt,
18    drm_amdgpu_memory_info,
19    drm_amdgpu_info_vce_clock_table,
20};
21use bindings::{
22    AMDGPU_INFO_NUM_BYTES_MOVED,
23    AMDGPU_INFO_NUM_EVICTIONS,
24    AMDGPU_INFO_VRAM_LOST_COUNTER,
25    AMDGPU_INFO_DEV_INFO,
26    AMDGPU_INFO_GDS_CONFIG,
27    AMDGPU_INFO_VRAM_GTT,
28    AMDGPU_INFO_MEMORY,
29    AMDGPU_INFO_VRAM_USAGE,
30    AMDGPU_INFO_VIS_VRAM_USAGE,
31    AMDGPU_INFO_GTT_USAGE,
32    AMDGPU_INFO_VCE_CLOCK_TABLE,
33    AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS,
34};
35use core::mem::{size_of, MaybeUninit};
36
37pub struct DeviceHandle {
38    #[cfg(feature = "dynamic_loading")]
39    pub(crate) libdrm: Arc<DynLibDrm>,
40    #[cfg(feature = "dynamic_loading")]
41    pub(crate) libdrm_amdgpu: Arc<DynLibDrmAmdgpu>,
42    pub(crate) amdgpu_dev: DEVICE_HANDLE,
43    pub(crate) fd: i32,
44}
45
46unsafe impl Send for DeviceHandle {}
47unsafe impl Sync for DeviceHandle {}
48
49use std::path::PathBuf;
50
51impl LibDrmAmdgpu {
52    pub fn init_device_handle(&self, fd: i32) -> Result<(DeviceHandle, u32, u32), i32> {
53        #[cfg(not(feature = "dynamic_loading"))]
54        let init = bindings::amdgpu_device_initialize;
55        #[cfg(feature = "dynamic_loading")]
56        let init = self.libdrm_amdgpu.amdgpu_device_initialize;
57
58        unsafe {
59            let mut amdgpu_dev: MaybeUninit<amdgpu_device_handle> = MaybeUninit::zeroed();
60            let mut major: MaybeUninit<u32> = MaybeUninit::zeroed();
61            let mut minor: MaybeUninit<u32> = MaybeUninit::zeroed();
62
63            let r = init(
64                fd,
65                major.as_mut_ptr(),
66                minor.as_mut_ptr(),
67                amdgpu_dev.as_mut_ptr(),
68            );
69
70            let [major, minor] = [major.assume_init(), minor.assume_init()];
71            let device_handle = DeviceHandle {
72                #[cfg(feature = "dynamic_loading")]
73                libdrm: self.libdrm.clone(),
74                #[cfg(feature = "dynamic_loading")]
75                libdrm_amdgpu: self.libdrm_amdgpu.clone(),
76                amdgpu_dev: amdgpu_dev.assume_init(),
77                fd,
78            };
79
80            query_error!(r);
81
82            Ok((device_handle, major, minor))
83        }
84    }
85}
86
87impl DeviceHandle {
88    /// Initialization.
89    /// Example of `fd`: `/dev/dri/renderD128`, `/dev/dri/by-path/pci-{[PCI::BUS]}-render`  
90    /// It may require a write option (`std::fs::OpenOptions::new().read(true).write(true)`)
91    /// for GUI context.  
92    /// ref: <https://gitlab.freedesktop.org/mesa/mesa/-/issues/2424>
93    #[cfg(not(feature = "dynamic_loading"))]
94    pub fn init(fd: i32) -> Result<(Self, u32, u32), i32> {
95        unsafe {
96            let mut amdgpu_dev: MaybeUninit<amdgpu_device_handle> = MaybeUninit::zeroed();
97            let mut major: MaybeUninit<u32> = MaybeUninit::zeroed();
98            let mut minor: MaybeUninit<u32> = MaybeUninit::zeroed();
99
100            let r = bindings::amdgpu_device_initialize(
101                fd,
102                major.as_mut_ptr(),
103                minor.as_mut_ptr(),
104                amdgpu_dev.as_mut_ptr(),
105            );
106
107            let [major, minor] = [major.assume_init(), minor.assume_init()];
108            let device_handle = Self {
109                amdgpu_dev: amdgpu_dev.assume_init(),
110                fd,
111            };
112
113            query_error!(r);
114
115            Ok((device_handle, major, minor))
116        }
117    }
118
119    fn deinit(&self) -> Result<i32, i32> {
120        #[cfg(not(feature = "dynamic_loading"))]
121        let func = bindings::amdgpu_device_deinitialize;
122        #[cfg(feature = "dynamic_loading")]
123        let func = self.libdrm_amdgpu.amdgpu_device_deinitialize;
124
125        let r = unsafe { func(self.amdgpu_dev) };
126
127        query_error!(r);
128
129        Ok(r)
130    }
131
132    pub fn get_fd(&self) -> i32 {
133        self.fd
134    }
135
136    /// Returns the result of reading the register at the specified offset.
137    /// If the offset is not allowed, returns `Err(i32)`.
138    pub fn read_mm_registers(&self, offset: u32) -> Result<u32, i32> {
139        #[cfg(not(feature = "dynamic_loading"))]
140        let func = bindings::amdgpu_read_mm_registers;
141        #[cfg(feature = "dynamic_loading")]
142        let func = self.libdrm_amdgpu.amdgpu_read_mm_registers;
143
144        unsafe {
145            let mut out: MaybeUninit<u32> = MaybeUninit::zeroed();
146
147            let r = func(
148                self.amdgpu_dev,
149                offset, // DWORD offset
150                1, // count
151                0xFFFF_FFFF, // instance mask, full mask
152                0, // flags
153                out.as_mut_ptr(),
154            );
155
156            let out = out.assume_init();
157
158            query_error!(r);
159
160            Ok(out)
161        }
162    }
163
164    pub fn query_gpu_info(&self) -> Result<amdgpu_gpu_info, i32> {
165        #[cfg(not(feature = "dynamic_loading"))]
166        let func = bindings::amdgpu_query_gpu_info;
167        #[cfg(feature = "dynamic_loading")]
168        let func = self.libdrm_amdgpu.amdgpu_query_gpu_info;
169
170        unsafe {
171            let mut gpu_info: MaybeUninit<amdgpu_gpu_info> = MaybeUninit::zeroed();
172
173            let r = func(self.amdgpu_dev, gpu_info.as_mut_ptr());
174
175            let gpu_info = gpu_info.assume_init();
176
177            query_error!(r);
178
179            Ok(gpu_info)
180        }
181    }
182
183    pub fn query_gds_info(&self) -> Result<amdgpu_gds_resource_info, i32> {
184        #[cfg(not(feature = "dynamic_loading"))]
185        let func = bindings::amdgpu_query_gds_info;
186        #[cfg(feature = "dynamic_loading")]
187        let func = self.libdrm_amdgpu.amdgpu_query_gds_info;
188
189        unsafe {
190            let mut gds_info: MaybeUninit<amdgpu_gds_resource_info> = MaybeUninit::zeroed();
191
192            let r = func(self.amdgpu_dev, gds_info.as_mut_ptr());
193
194            let gds_info = gds_info.assume_init();
195
196            query_error!(r);
197
198            Ok(gds_info)
199        }
200    }
201
202    pub fn query_sw_info(&self, info: amdgpu_sw_info) -> Result<u32, i32> {
203        #[cfg(not(feature = "dynamic_loading"))]
204        let func = bindings::amdgpu_query_sw_info;
205        #[cfg(feature = "dynamic_loading")]
206        let func = self.libdrm_amdgpu.amdgpu_query_sw_info;
207
208        unsafe {
209            let mut val: MaybeUninit<u32> = MaybeUninit::zeroed();
210
211            let r = func(
212                self.amdgpu_dev,
213                info as u32,
214                val.as_mut_ptr() as *mut ::core::ffi::c_void,
215            );
216
217            let val = val.assume_init();
218
219            query_error!(r);
220
221            Ok(val)
222        }
223    }
224
225    pub(crate) fn query<T>(&self, info_id: ::core::ffi::c_uint) -> Result<T, i32> {
226        #[cfg(not(feature = "dynamic_loading"))]
227        let func = bindings::amdgpu_query_info;
228        #[cfg(feature = "dynamic_loading")]
229        let func = self.libdrm_amdgpu.amdgpu_query_info;
230
231        unsafe {
232            let mut dev: MaybeUninit<T> = MaybeUninit::zeroed();
233
234            let r = func(
235                self.amdgpu_dev,
236                info_id,
237                size_of::<T>() as u32,
238                dev.as_mut_ptr() as *mut ::core::ffi::c_void,
239            );
240
241            let dev = dev.assume_init();
242
243            query_error!(r);
244
245            Ok(dev)
246        }
247    }
248
249    pub fn device_info(&self) -> Result<drm_amdgpu_info_device, i32> {
250        Self::query(self, AMDGPU_INFO_DEV_INFO)
251    }
252
253    /// Note: `usable_heap_size` equal `real_size - pin_size - reserved_size`, is not fixed.
254    pub fn vram_gtt_info(&self) -> Result<drm_amdgpu_info_vram_gtt, i32> {
255        Self::query(self, AMDGPU_INFO_VRAM_GTT)
256    }
257
258    pub fn memory_info(&self) -> Result<drm_amdgpu_memory_info, i32> {
259        Self::query(self, AMDGPU_INFO_MEMORY)
260    }
261
262    pub fn vram_usage_info(&self) -> Result<u64, i32> {
263        Self::query(self, AMDGPU_INFO_VRAM_USAGE)
264    }
265
266    pub fn vis_vram_usage_info(&self) -> Result<u64, i32> {
267        Self::query(self, AMDGPU_INFO_VIS_VRAM_USAGE)
268    }
269
270    pub fn gtt_usage_info(&self) -> Result<u64, i32> {
271        Self::query(self, AMDGPU_INFO_GTT_USAGE)
272    }
273
274    pub fn gds_info(&self) -> Result<drm_amdgpu_info_gds, i32> {
275        Self::query(self, AMDGPU_INFO_GDS_CONFIG)
276    }
277
278    /// AMDGPU driver returns invalid [drm_amdgpu_info_vce_clock_table].
279    /// ref: <https://gitlab.freedesktop.org/drm/amd/-/issues/2391>
280    pub fn vce_clock_info(&self) -> Result<drm_amdgpu_info_vce_clock_table, i32> {
281        Self::query(self, AMDGPU_INFO_VCE_CLOCK_TABLE)
282    }
283
284    /// Number of VRAM page faults on CPU access
285    pub fn num_vram_cpu_page_faults(&self) -> Result<u64, i32> {
286        Self::query(self, AMDGPU_INFO_NUM_VRAM_CPU_PAGE_FAULTS)
287    }
288
289    /// Number of bytes moved for TTM migration
290    pub fn num_bytes_moved(&self) -> Result<u64, i32> {
291        Self::query(self, AMDGPU_INFO_NUM_BYTES_MOVED)
292    }
293
294    /// Number of TTM buffer evictions
295    pub fn num_evictions(&self) -> Result<u64, i32> {
296        Self::query(self, AMDGPU_INFO_NUM_EVICTIONS)
297    }
298
299    pub fn vram_lost_counter(&self) -> Result<u32, i32> {
300        Self::query(self, AMDGPU_INFO_VRAM_LOST_COUNTER)
301    }
302
303    /// Get [PCI::BUS_INFO]
304    pub fn get_pci_bus_info(&self) -> Result<PCI::BUS_INFO, i32> {
305        self.drm_get_device2()
306    }
307
308    fn drm_get_device2(&self) -> Result<PCI::BUS_INFO, i32> {
309        let pci = unsafe {
310            let mut dev_info = self.__drmGetDevice2(self.fd, 0)?;
311            let pci = core::ptr::read((*dev_info).businfo.pci);
312            self.__drmFreeDevice(&mut dev_info);
313
314            pci
315        };
316
317        Ok(PCI::BUS_INFO {
318            domain: pci.domain,
319            bus: pci.bus,
320            dev: pci.dev,
321            func: pci.func,
322        })
323    }
324
325    unsafe fn __drmGetDevice2(&self, fd: ::core::ffi::c_int, flags: u32) -> Result<drmDevicePtr, i32> { unsafe {
326        #[cfg(not(feature = "dynamic_loading"))]
327        let func = bindings::drmGetDevice2;
328        #[cfg(feature = "dynamic_loading")]
329        let func = self.libdrm.drmGetDevice2;
330
331        let mut drm_dev_info: MaybeUninit<drmDevicePtr> = MaybeUninit::uninit();
332
333        let r = func(fd, flags, drm_dev_info.as_mut_ptr());
334
335        let drm_dev_info = drm_dev_info.assume_init();
336
337        if drm_dev_info.is_null() {
338            return Err(r);
339        }
340
341        query_error!(r);
342
343        Ok(drm_dev_info)
344    }}
345
346    unsafe fn __drmFreeDevice(&self, device: *mut drmDevicePtr) { unsafe {
347        #[cfg(not(feature = "dynamic_loading"))]
348        let func = bindings::drmFreeDevice;
349        #[cfg(feature = "dynamic_loading")]
350        let func = self.libdrm.drmFreeDevice;
351
352        func(device)
353    }}
354
355    fn get_min_max_clock_from_dpm<P: Into<PathBuf>>(
356        &self,
357        sysfs_path: P,
358    ) -> Option<[u32; 2]> {
359        let parse_line = |s: &str| -> Option<u32> {
360            s.split(' ').nth(1)?.trim_end_matches("Mhz").parse::<u32>().ok()
361        };
362
363        AMDGPU::get_min_max_from_dpm(sysfs_path.into(), parse_line)
364    }
365
366    /// Get the min/max gpu core clock (MHz) from sysfs (`pp_dpm_mclk`)
367    pub fn get_min_max_memory_clock_from_dpm<P: Into<PathBuf>>(
368        &self,
369        path: P
370    ) -> Option<[u32; 2]> {
371        self.get_min_max_clock_from_dpm(path.into().join("pp_dpm_mclk"))
372    }
373
374    /// Get the min/max gpu core clock (MHz) from sysfs (`pp_dpm_sclk`)
375    pub fn get_min_max_gpu_clock_from_dpm<P: Into<PathBuf>>(
376        &self,
377        path: P
378    ) -> Option<[u32; 2]> {
379        self.get_min_max_clock_from_dpm(path.into().join("pp_dpm_sclk"))
380    }
381
382    /// Get the min/max gpu core clock (MHz) from sysfs (`pp_dpm_mclk`)
383    pub fn get_min_max_memory_clock_from_sysfs<P: Into<PathBuf>>(
384        &self,
385        path: P
386    ) -> Option<(u32, u32)> {
387        let tmp = self.get_min_max_clock_from_dpm(path.into().join("pp_dpm_mclk"))?;
388
389        Some((tmp[0], tmp[1]))
390    }
391
392    /// Get the min/max gpu core clock (MHz) from sysfs (`pp_dpm_mclk`)
393    pub fn get_min_max_memory_clock(&self) -> Option<(u32, u32)> {
394        let sysfs_path = self.get_sysfs_path().ok()?;
395        self.get_min_max_memory_clock_from_sysfs(sysfs_path)
396    }
397
398    /// Get the min/max gpu core clock (MHz) from sysfs (`pp_dpm_sclk`)
399    pub fn get_min_max_gpu_clock_from_sysfs<P: Into<PathBuf>>(
400        &self,
401        path: P
402    ) -> Option<(u32, u32)> {
403        let tmp = self.get_min_max_clock_from_dpm(path.into().join("pp_dpm_sclk"))?;
404
405        Some((tmp[0], tmp[1]))
406    }
407
408    /// Get the min/max gpu core clock (MHz) from sysfs (`pp_dpm_sclk`)
409    pub fn get_min_max_gpu_clock(&self) -> Option<(u32, u32)> {
410        let sysfs_path = self.get_sysfs_path().ok()?;
411        self.get_min_max_gpu_clock_from_sysfs(sysfs_path)
412    }
413
414    /// 
415    pub fn get_sysfs_path(&self) -> Result<PathBuf, i32> {
416        let path = self.get_pci_bus_info()?.get_sysfs_path();
417
418        Ok(path)
419    }
420
421    pub(crate) fn get_sysfs_path_io(&self) -> std::io::Result<PathBuf> {
422        let path = self
423            .get_pci_bus_info()
424            .map_err(|_| std::io::Error::new(std::io::ErrorKind::Other, "Failed get_pci_bus_info"))?
425            .get_sysfs_path();
426
427        Ok(path)
428    }
429
430    /// 
431    pub fn get_hwmon_path(&self) -> Option<PathBuf> {
432        self.get_pci_bus_info().ok()?.get_hwmon_path()
433    }
434
435    /// ref: drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c
436    /// ref: <https://github.com/RadeonOpenCompute/rocm_smi_lib/blob/master/python_smi_tools/rocm_smi.py>
437    pub fn check_if_secondary_die(&self) -> bool {
438        let Some(power_cap) = self.get_power_cap() else { return false };
439
440        power_cap.check_if_secondary_die()
441    }
442
443    pub fn get_min_max_link_info_from_dpm(&self) -> Option<[PCI::LINK; 2]> {
444        let pci_bus = self.get_pci_bus_info().ok()?;
445
446        pci_bus.get_min_max_link_info_from_dpm()
447    }
448
449    /// [PCI::BUS_INFO::get_max_gpu_link]
450    pub fn get_max_gpu_link(&self) -> Option<PCI::LINK> {
451        let pci_bus = self.get_pci_bus_info().ok()?;
452
453        pci_bus.get_max_gpu_link()
454    }
455
456    /// [PCI::BUS_INFO::get_max_system_link]
457    pub fn get_max_system_link(&self) -> Option<PCI::LINK> {
458        let pci_bus = self.get_pci_bus_info().ok()?;
459
460        pci_bus.get_max_system_link()
461    }
462}
463
464impl Drop for DeviceHandle {
465    fn drop(&mut self) {
466        self.deinit().unwrap();
467    }
468}
469
470impl drm_amdgpu_memory_info {
471    /// The AMDGPU driver allocates part of VRAM to pre-OS buffer (vbios, frame buffer)
472    /// if VRAM is larger than 8GiB
473    /// ref: drivers/gpu/drm/amd/amdgpu/amdgpu_gmc.c  
474    /// ref: <https://gitlab.freedesktop.org/mesa/mesa/blob/main/src/amd/common/ac_gpu_info.c>  
475    pub fn check_resizable_bar(&self) -> bool {
476        (self.vram.total_heap_size * 9 / 10) <= self.cpu_accessible_vram.total_heap_size
477    }
478}
479
480#[repr(u32)]
481pub enum amdgpu_sw_info {
482    address32_hi = 0,
483}