1*82b78182SAsahi Lina // SPDX-License-Identifier: GPL-2.0 OR MIT 2*82b78182SAsahi Lina 3*82b78182SAsahi Lina #![cfg(CONFIG_DRM_GPUVM = "y")] 4*82b78182SAsahi Lina 5*82b78182SAsahi Lina //! DRM GPUVM in immediate mode 6*82b78182SAsahi Lina //! 7*82b78182SAsahi Lina //! Rust abstractions for using GPUVM in immediate mode. This is when the GPUVM state is updated 8*82b78182SAsahi Lina //! during `run_job()`, i.e., in the DMA fence signalling critical path, to ensure that the GPUVM 9*82b78182SAsahi Lina //! and the GPU's virtual address space has the same state at all times. 10*82b78182SAsahi Lina //! 11*82b78182SAsahi Lina //! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h) 12*82b78182SAsahi Lina 13*82b78182SAsahi Lina use kernel::{ 14*82b78182SAsahi Lina alloc::AllocError, 15*82b78182SAsahi Lina bindings, 16*82b78182SAsahi Lina drm, 17*82b78182SAsahi Lina drm::gem::IntoGEMObject, 18*82b78182SAsahi Lina prelude::*, 19*82b78182SAsahi Lina sync::aref::{ 20*82b78182SAsahi Lina ARef, 21*82b78182SAsahi Lina AlwaysRefCounted, // 22*82b78182SAsahi Lina }, 23*82b78182SAsahi Lina types::Opaque, // 24*82b78182SAsahi Lina }; 25*82b78182SAsahi Lina 26*82b78182SAsahi Lina use core::{ 27*82b78182SAsahi Lina cell::UnsafeCell, 28*82b78182SAsahi Lina ops::{ 29*82b78182SAsahi Lina Deref, 30*82b78182SAsahi Lina Range, // 31*82b78182SAsahi Lina }, 32*82b78182SAsahi Lina ptr::NonNull, // 33*82b78182SAsahi Lina }; 34*82b78182SAsahi Lina 35*82b78182SAsahi Lina /// A DRM GPU VA manager. 36*82b78182SAsahi Lina /// 37*82b78182SAsahi Lina /// This object is refcounted, but the locations of mapped ranges may only be accessed or changed 38*82b78182SAsahi Lina /// via the special unique handle [`UniqueRefGpuVm`]. 39*82b78182SAsahi Lina /// 40*82b78182SAsahi Lina /// # Invariants 41*82b78182SAsahi Lina /// 42*82b78182SAsahi Lina /// * Stored in an allocation managed by the refcount in `self.vm`. 43*82b78182SAsahi Lina /// * Access to `data` and the gpuvm interval tree is controlled via the [`UniqueRefGpuVm`] type. 44*82b78182SAsahi Lina /// * Does not contain any sparse `GpuVa` instances. 45*82b78182SAsahi Lina #[pin_data] 46*82b78182SAsahi Lina pub struct GpuVm<T: DriverGpuVm> { 47*82b78182SAsahi Lina #[pin] 48*82b78182SAsahi Lina vm: Opaque<bindings::drm_gpuvm>, 49*82b78182SAsahi Lina /// Accessed only through the [`UniqueRefGpuVm`] reference. 50*82b78182SAsahi Lina data: UnsafeCell<T>, 51*82b78182SAsahi Lina } 52*82b78182SAsahi Lina 53*82b78182SAsahi Lina // SAFETY: The GPUVM api does not assume that it is tied to a specific thread. The destructor will 54*82b78182SAsahi Lina // drop the `data` field, which is okay because it is guaranteed `Send` by the `DriverGpuVm` trait. 55*82b78182SAsahi Lina unsafe impl<T: DriverGpuVm> Send for GpuVm<T> {} 56*82b78182SAsahi Lina // SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel. 57*82b78182SAsahi Lina unsafe impl<T: DriverGpuVm> Sync for GpuVm<T> {} 58*82b78182SAsahi Lina 59*82b78182SAsahi Lina // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`. 60*82b78182SAsahi Lina unsafe impl<T: DriverGpuVm> AlwaysRefCounted for GpuVm<T> { 61*82b78182SAsahi Lina fn inc_ref(&self) { 62*82b78182SAsahi Lina // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`. 63*82b78182SAsahi Lina unsafe { bindings::drm_gpuvm_get(self.vm.get()) }; 64*82b78182SAsahi Lina } 65*82b78182SAsahi Lina 66*82b78182SAsahi Lina unsafe fn dec_ref(obj: NonNull<Self>) { 67*82b78182SAsahi Lina // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`. 68*82b78182SAsahi Lina unsafe { bindings::drm_gpuvm_put((*obj.as_ptr()).vm.get()) }; 69*82b78182SAsahi Lina } 70*82b78182SAsahi Lina } 71*82b78182SAsahi Lina 72*82b78182SAsahi Lina impl<T: DriverGpuVm> PartialEq for GpuVm<T> { 73*82b78182SAsahi Lina #[inline] 74*82b78182SAsahi Lina fn eq(&self, other: &Self) -> bool { 75*82b78182SAsahi Lina core::ptr::eq(self.as_raw(), other.as_raw()) 76*82b78182SAsahi Lina } 77*82b78182SAsahi Lina } 78*82b78182SAsahi Lina impl<T: DriverGpuVm> Eq for GpuVm<T> {} 79*82b78182SAsahi Lina 80*82b78182SAsahi Lina impl<T: DriverGpuVm> GpuVm<T> { 81*82b78182SAsahi Lina const fn vtable() -> &'static bindings::drm_gpuvm_ops { 82*82b78182SAsahi Lina &bindings::drm_gpuvm_ops { 83*82b78182SAsahi Lina vm_free: Some(Self::vm_free), 84*82b78182SAsahi Lina op_alloc: None, 85*82b78182SAsahi Lina op_free: None, 86*82b78182SAsahi Lina vm_bo_alloc: None, 87*82b78182SAsahi Lina vm_bo_free: None, 88*82b78182SAsahi Lina vm_bo_validate: None, 89*82b78182SAsahi Lina sm_step_map: None, 90*82b78182SAsahi Lina sm_step_unmap: None, 91*82b78182SAsahi Lina sm_step_remap: None, 92*82b78182SAsahi Lina } 93*82b78182SAsahi Lina } 94*82b78182SAsahi Lina 95*82b78182SAsahi Lina /// Creates a GPUVM instance. 96*82b78182SAsahi Lina #[expect(clippy::new_ret_no_self)] 97*82b78182SAsahi Lina pub fn new<E>( 98*82b78182SAsahi Lina name: &'static CStr, 99*82b78182SAsahi Lina dev: &drm::Device<T::Driver>, 100*82b78182SAsahi Lina r_obj: &T::Object, 101*82b78182SAsahi Lina range: Range<u64>, 102*82b78182SAsahi Lina reserve_range: Range<u64>, 103*82b78182SAsahi Lina data: T, 104*82b78182SAsahi Lina ) -> Result<UniqueRefGpuVm<T>, E> 105*82b78182SAsahi Lina where 106*82b78182SAsahi Lina E: From<AllocError>, 107*82b78182SAsahi Lina E: From<core::convert::Infallible>, 108*82b78182SAsahi Lina { 109*82b78182SAsahi Lina let obj = KBox::try_pin_init::<E>( 110*82b78182SAsahi Lina try_pin_init!(Self { 111*82b78182SAsahi Lina data: UnsafeCell::new(data), 112*82b78182SAsahi Lina vm <- Opaque::ffi_init(|vm| { 113*82b78182SAsahi Lina // SAFETY: These arguments are valid. `vm` is valid until refcount drops to 114*82b78182SAsahi Lina // zero. The `vm` is zeroed before calling this method by `__GFP_ZERO` flag 115*82b78182SAsahi Lina // below. 116*82b78182SAsahi Lina unsafe { 117*82b78182SAsahi Lina bindings::drm_gpuvm_init( 118*82b78182SAsahi Lina vm, 119*82b78182SAsahi Lina name.as_char_ptr(), 120*82b78182SAsahi Lina bindings::drm_gpuvm_flags_DRM_GPUVM_IMMEDIATE_MODE 121*82b78182SAsahi Lina | bindings::drm_gpuvm_flags_DRM_GPUVM_RESV_PROTECTED, 122*82b78182SAsahi Lina dev.as_raw(), 123*82b78182SAsahi Lina r_obj.as_raw(), 124*82b78182SAsahi Lina range.start, 125*82b78182SAsahi Lina range.end - range.start, 126*82b78182SAsahi Lina reserve_range.start, 127*82b78182SAsahi Lina reserve_range.end - reserve_range.start, 128*82b78182SAsahi Lina const { Self::vtable() }, 129*82b78182SAsahi Lina ) 130*82b78182SAsahi Lina } 131*82b78182SAsahi Lina }), 132*82b78182SAsahi Lina }? E), 133*82b78182SAsahi Lina GFP_KERNEL | __GFP_ZERO, 134*82b78182SAsahi Lina )?; 135*82b78182SAsahi Lina // SAFETY: This transfers the initial refcount to the ARef. 136*82b78182SAsahi Lina let aref = unsafe { 137*82b78182SAsahi Lina ARef::from_raw(NonNull::new_unchecked(KBox::into_raw( 138*82b78182SAsahi Lina Pin::into_inner_unchecked(obj), 139*82b78182SAsahi Lina ))) 140*82b78182SAsahi Lina }; 141*82b78182SAsahi Lina // INVARIANT: This reference is unique. 142*82b78182SAsahi Lina Ok(UniqueRefGpuVm(aref)) 143*82b78182SAsahi Lina } 144*82b78182SAsahi Lina 145*82b78182SAsahi Lina /// Access this [`GpuVm`] from a raw pointer. 146*82b78182SAsahi Lina /// 147*82b78182SAsahi Lina /// # Safety 148*82b78182SAsahi Lina /// 149*82b78182SAsahi Lina /// The pointer must reference the `struct drm_gpuvm` in a valid [`GpuVm<T>`] that remains 150*82b78182SAsahi Lina /// valid for at least `'a`. 151*82b78182SAsahi Lina #[inline] 152*82b78182SAsahi Lina pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm) -> &'a Self { 153*82b78182SAsahi Lina // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`. Caller ensures the 154*82b78182SAsahi Lina // pointer is valid for 'a. 155*82b78182SAsahi Lina unsafe { &*kernel::container_of!(Opaque::cast_from(ptr), Self, vm) } 156*82b78182SAsahi Lina } 157*82b78182SAsahi Lina 158*82b78182SAsahi Lina /// Returns a raw pointer to the embedded `struct drm_gpuvm`. 159*82b78182SAsahi Lina #[inline] 160*82b78182SAsahi Lina pub fn as_raw(&self) -> *mut bindings::drm_gpuvm { 161*82b78182SAsahi Lina self.vm.get() 162*82b78182SAsahi Lina } 163*82b78182SAsahi Lina 164*82b78182SAsahi Lina /// The start of the VA space. 165*82b78182SAsahi Lina #[inline] 166*82b78182SAsahi Lina pub fn va_start(&self) -> u64 { 167*82b78182SAsahi Lina // SAFETY: The `mm_start` field is immutable. 168*82b78182SAsahi Lina unsafe { (*self.as_raw()).mm_start } 169*82b78182SAsahi Lina } 170*82b78182SAsahi Lina 171*82b78182SAsahi Lina /// The length of the GPU's virtual address space. 172*82b78182SAsahi Lina #[inline] 173*82b78182SAsahi Lina pub fn va_length(&self) -> u64 { 174*82b78182SAsahi Lina // SAFETY: The `mm_range` field is immutable. 175*82b78182SAsahi Lina unsafe { (*self.as_raw()).mm_range } 176*82b78182SAsahi Lina } 177*82b78182SAsahi Lina 178*82b78182SAsahi Lina /// Returns the range of the GPU virtual address space. 179*82b78182SAsahi Lina #[inline] 180*82b78182SAsahi Lina pub fn va_range(&self) -> Range<u64> { 181*82b78182SAsahi Lina let start = self.va_start(); 182*82b78182SAsahi Lina // OVERFLOW: This reconstructs the Range<u64> passed to the constructor, so it won't fail. 183*82b78182SAsahi Lina let end = start + self.va_length(); 184*82b78182SAsahi Lina Range { start, end } 185*82b78182SAsahi Lina } 186*82b78182SAsahi Lina 187*82b78182SAsahi Lina /// Clean up buffer objects that are no longer used. 188*82b78182SAsahi Lina #[inline] 189*82b78182SAsahi Lina pub fn deferred_cleanup(&self) { 190*82b78182SAsahi Lina // SAFETY: This GPUVM uses immediate mode. 191*82b78182SAsahi Lina unsafe { bindings::drm_gpuvm_bo_deferred_cleanup(self.as_raw()) } 192*82b78182SAsahi Lina } 193*82b78182SAsahi Lina 194*82b78182SAsahi Lina /// Check if this GEM object is an external object for this GPUVM. 195*82b78182SAsahi Lina #[inline] 196*82b78182SAsahi Lina pub fn is_extobj(&self, obj: &T::Object) -> bool { 197*82b78182SAsahi Lina // SAFETY: We may call this with any GPUVM and GEM object. 198*82b78182SAsahi Lina unsafe { bindings::drm_gpuvm_is_extobj(self.as_raw(), obj.as_raw()) } 199*82b78182SAsahi Lina } 200*82b78182SAsahi Lina 201*82b78182SAsahi Lina /// Free this GPUVM. 202*82b78182SAsahi Lina /// 203*82b78182SAsahi Lina /// # Safety 204*82b78182SAsahi Lina /// 205*82b78182SAsahi Lina /// Called when refcount hits zero. 206*82b78182SAsahi Lina unsafe extern "C" fn vm_free(me: *mut bindings::drm_gpuvm) { 207*82b78182SAsahi Lina // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`. 208*82b78182SAsahi Lina let me = unsafe { kernel::container_of!(Opaque::cast_from(me), Self, vm).cast_mut() }; 209*82b78182SAsahi Lina // SAFETY: By type invariants we can free it when refcount hits zero. 210*82b78182SAsahi Lina drop(unsafe { KBox::from_raw(me) }) 211*82b78182SAsahi Lina } 212*82b78182SAsahi Lina } 213*82b78182SAsahi Lina 214*82b78182SAsahi Lina /// The manager for a GPUVM. 215*82b78182SAsahi Lina pub trait DriverGpuVm: Sized + Send { 216*82b78182SAsahi Lina /// Parent `Driver` for this object. 217*82b78182SAsahi Lina type Driver: drm::Driver<Object = Self::Object>; 218*82b78182SAsahi Lina 219*82b78182SAsahi Lina /// The kind of GEM object stored in this GPUVM. 220*82b78182SAsahi Lina type Object: IntoGEMObject; 221*82b78182SAsahi Lina } 222*82b78182SAsahi Lina 223*82b78182SAsahi Lina /// The core of the DRM GPU VA manager. 224*82b78182SAsahi Lina /// 225*82b78182SAsahi Lina /// This object is a unique reference to the VM that can access the interval tree and the Rust 226*82b78182SAsahi Lina /// `data` field. 227*82b78182SAsahi Lina /// 228*82b78182SAsahi Lina /// # Invariants 229*82b78182SAsahi Lina /// 230*82b78182SAsahi Lina /// Each `GpuVm` instance has at most one `UniqueRefGpuVm` reference. 231*82b78182SAsahi Lina pub struct UniqueRefGpuVm<T: DriverGpuVm>(ARef<GpuVm<T>>); 232*82b78182SAsahi Lina 233*82b78182SAsahi Lina // SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel, and 234*82b78182SAsahi Lina // concurrent access to `data` is safe due to the `T: Sync` requirement. 235*82b78182SAsahi Lina unsafe impl<T: DriverGpuVm + Sync> Sync for UniqueRefGpuVm<T> {} 236*82b78182SAsahi Lina 237*82b78182SAsahi Lina impl<T: DriverGpuVm> UniqueRefGpuVm<T> { 238*82b78182SAsahi Lina /// Access the data owned by this `UniqueRefGpuVm` immutably. 239*82b78182SAsahi Lina #[inline] 240*82b78182SAsahi Lina pub fn data_ref(&self) -> &T { 241*82b78182SAsahi Lina // SAFETY: By the type invariants we may access `data`. 242*82b78182SAsahi Lina unsafe { &*self.0.data.get() } 243*82b78182SAsahi Lina } 244*82b78182SAsahi Lina 245*82b78182SAsahi Lina /// Access the data owned by this `UniqueRefGpuVm` mutably. 246*82b78182SAsahi Lina #[inline] 247*82b78182SAsahi Lina pub fn data(&mut self) -> &mut T { 248*82b78182SAsahi Lina // SAFETY: By the type invariants we may access `data`. 249*82b78182SAsahi Lina unsafe { &mut *self.0.data.get() } 250*82b78182SAsahi Lina } 251*82b78182SAsahi Lina } 252*82b78182SAsahi Lina 253*82b78182SAsahi Lina impl<T: DriverGpuVm> Deref for UniqueRefGpuVm<T> { 254*82b78182SAsahi Lina type Target = GpuVm<T>; 255*82b78182SAsahi Lina 256*82b78182SAsahi Lina #[inline] 257*82b78182SAsahi Lina fn deref(&self) -> &GpuVm<T> { 258*82b78182SAsahi Lina &self.0 259*82b78182SAsahi Lina } 260*82b78182SAsahi Lina } 261