182b78182SAsahi Lina // SPDX-License-Identifier: GPL-2.0 OR MIT 282b78182SAsahi Lina 382b78182SAsahi Lina #![cfg(CONFIG_DRM_GPUVM = "y")] 482b78182SAsahi Lina 582b78182SAsahi Lina //! DRM GPUVM in immediate mode 682b78182SAsahi Lina //! 782b78182SAsahi Lina //! Rust abstractions for using GPUVM in immediate mode. This is when the GPUVM state is updated 882b78182SAsahi Lina //! during `run_job()`, i.e., in the DMA fence signalling critical path, to ensure that the GPUVM 982b78182SAsahi Lina //! and the GPU's virtual address space has the same state at all times. 1082b78182SAsahi Lina //! 1182b78182SAsahi Lina //! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h) 1282b78182SAsahi Lina 1382b78182SAsahi Lina use kernel::{ 145540a9c7SAlice Ryhl alloc::{ 155540a9c7SAlice Ryhl AllocError, 165540a9c7SAlice Ryhl Flags as AllocFlags, // 175540a9c7SAlice Ryhl }, 1882b78182SAsahi Lina bindings, 1982b78182SAsahi Lina drm, 2082b78182SAsahi Lina drm::gem::IntoGEMObject, 21*dc384604SAlice Ryhl error::to_result, 2282b78182SAsahi Lina prelude::*, 2382b78182SAsahi Lina sync::aref::{ 2482b78182SAsahi Lina ARef, 2582b78182SAsahi Lina AlwaysRefCounted, // 2682b78182SAsahi Lina }, 2782b78182SAsahi Lina types::Opaque, // 2882b78182SAsahi Lina }; 2982b78182SAsahi Lina 3082b78182SAsahi Lina use core::{ 3182b78182SAsahi Lina cell::UnsafeCell, 32*dc384604SAlice Ryhl marker::PhantomData, 335540a9c7SAlice Ryhl mem::{ 345540a9c7SAlice Ryhl ManuallyDrop, 355540a9c7SAlice Ryhl MaybeUninit, // 365540a9c7SAlice Ryhl }, 3782b78182SAsahi Lina ops::{ 3882b78182SAsahi Lina Deref, 395540a9c7SAlice Ryhl DerefMut, 4082b78182SAsahi Lina Range, // 4182b78182SAsahi Lina }, 4271a39211SAlice Ryhl ptr::{ 4371a39211SAlice Ryhl self, 4471a39211SAlice Ryhl NonNull, // 4571a39211SAlice Ryhl }, // 4682b78182SAsahi Lina }; 4782b78182SAsahi Lina 48*dc384604SAlice Ryhl mod sm_ops; 49*dc384604SAlice Ryhl pub use self::sm_ops::*; 505540a9c7SAlice Ryhl 5171a39211SAlice Ryhl mod vm_bo; 5271a39211SAlice Ryhl pub use self::vm_bo::*; 5371a39211SAlice Ryhl 54*dc384604SAlice Ryhl mod va; 55*dc384604SAlice Ryhl pub use self::va::*; 56*dc384604SAlice Ryhl 5782b78182SAsahi Lina /// A DRM GPU VA manager. 5882b78182SAsahi Lina /// 5982b78182SAsahi Lina /// This object is refcounted, but the locations of mapped ranges may only be accessed or changed 6082b78182SAsahi Lina /// via the special unique handle [`UniqueRefGpuVm`]. 6182b78182SAsahi Lina /// 6282b78182SAsahi Lina /// # Invariants 6382b78182SAsahi Lina /// 6482b78182SAsahi Lina /// * Stored in an allocation managed by the refcount in `self.vm`. 6582b78182SAsahi Lina /// * Access to `data` and the gpuvm interval tree is controlled via the [`UniqueRefGpuVm`] type. 665540a9c7SAlice Ryhl /// * Does not contain any sparse [`GpuVa<T>`] instances. 6782b78182SAsahi Lina #[pin_data] 6882b78182SAsahi Lina pub struct GpuVm<T: DriverGpuVm> { 6982b78182SAsahi Lina #[pin] 7082b78182SAsahi Lina vm: Opaque<bindings::drm_gpuvm>, 7182b78182SAsahi Lina /// Accessed only through the [`UniqueRefGpuVm`] reference. 7282b78182SAsahi Lina data: UnsafeCell<T>, 7382b78182SAsahi Lina } 7482b78182SAsahi Lina 7582b78182SAsahi Lina // SAFETY: The GPUVM api does not assume that it is tied to a specific thread. The destructor will 7682b78182SAsahi Lina // drop the `data` field, which is okay because it is guaranteed `Send` by the `DriverGpuVm` trait. 7782b78182SAsahi Lina unsafe impl<T: DriverGpuVm> Send for GpuVm<T> {} 7882b78182SAsahi Lina // SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel. 7982b78182SAsahi Lina unsafe impl<T: DriverGpuVm> Sync for GpuVm<T> {} 8082b78182SAsahi Lina 8182b78182SAsahi Lina // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`. 8282b78182SAsahi Lina unsafe impl<T: DriverGpuVm> AlwaysRefCounted for GpuVm<T> { 8382b78182SAsahi Lina fn inc_ref(&self) { 8482b78182SAsahi Lina // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`. 8582b78182SAsahi Lina unsafe { bindings::drm_gpuvm_get(self.vm.get()) }; 8682b78182SAsahi Lina } 8782b78182SAsahi Lina 8882b78182SAsahi Lina unsafe fn dec_ref(obj: NonNull<Self>) { 8982b78182SAsahi Lina // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`. 9082b78182SAsahi Lina unsafe { bindings::drm_gpuvm_put((*obj.as_ptr()).vm.get()) }; 9182b78182SAsahi Lina } 9282b78182SAsahi Lina } 9382b78182SAsahi Lina 9482b78182SAsahi Lina impl<T: DriverGpuVm> PartialEq for GpuVm<T> { 9582b78182SAsahi Lina #[inline] 9682b78182SAsahi Lina fn eq(&self, other: &Self) -> bool { 9782b78182SAsahi Lina core::ptr::eq(self.as_raw(), other.as_raw()) 9882b78182SAsahi Lina } 9982b78182SAsahi Lina } 10082b78182SAsahi Lina impl<T: DriverGpuVm> Eq for GpuVm<T> {} 10182b78182SAsahi Lina 10282b78182SAsahi Lina impl<T: DriverGpuVm> GpuVm<T> { 10382b78182SAsahi Lina const fn vtable() -> &'static bindings::drm_gpuvm_ops { 10482b78182SAsahi Lina &bindings::drm_gpuvm_ops { 10582b78182SAsahi Lina vm_free: Some(Self::vm_free), 10682b78182SAsahi Lina op_alloc: None, 10782b78182SAsahi Lina op_free: None, 10871a39211SAlice Ryhl vm_bo_alloc: GpuVmBo::<T>::ALLOC_FN, 10971a39211SAlice Ryhl vm_bo_free: GpuVmBo::<T>::FREE_FN, 11082b78182SAsahi Lina vm_bo_validate: None, 11182b78182SAsahi Lina sm_step_map: None, 112*dc384604SAlice Ryhl sm_step_unmap: Some(Self::sm_step_unmap), 113*dc384604SAlice Ryhl sm_step_remap: Some(Self::sm_step_remap), 11482b78182SAsahi Lina } 11582b78182SAsahi Lina } 11682b78182SAsahi Lina 11782b78182SAsahi Lina /// Creates a GPUVM instance. 11882b78182SAsahi Lina #[expect(clippy::new_ret_no_self)] 11982b78182SAsahi Lina pub fn new<E>( 12082b78182SAsahi Lina name: &'static CStr, 12182b78182SAsahi Lina dev: &drm::Device<T::Driver>, 12282b78182SAsahi Lina r_obj: &T::Object, 12382b78182SAsahi Lina range: Range<u64>, 12482b78182SAsahi Lina reserve_range: Range<u64>, 12582b78182SAsahi Lina data: T, 12682b78182SAsahi Lina ) -> Result<UniqueRefGpuVm<T>, E> 12782b78182SAsahi Lina where 12882b78182SAsahi Lina E: From<AllocError>, 12982b78182SAsahi Lina E: From<core::convert::Infallible>, 13082b78182SAsahi Lina { 13182b78182SAsahi Lina let obj = KBox::try_pin_init::<E>( 13282b78182SAsahi Lina try_pin_init!(Self { 13382b78182SAsahi Lina data: UnsafeCell::new(data), 13482b78182SAsahi Lina vm <- Opaque::ffi_init(|vm| { 13582b78182SAsahi Lina // SAFETY: These arguments are valid. `vm` is valid until refcount drops to 13682b78182SAsahi Lina // zero. The `vm` is zeroed before calling this method by `__GFP_ZERO` flag 13782b78182SAsahi Lina // below. 13882b78182SAsahi Lina unsafe { 13982b78182SAsahi Lina bindings::drm_gpuvm_init( 14082b78182SAsahi Lina vm, 14182b78182SAsahi Lina name.as_char_ptr(), 14282b78182SAsahi Lina bindings::drm_gpuvm_flags_DRM_GPUVM_IMMEDIATE_MODE 14382b78182SAsahi Lina | bindings::drm_gpuvm_flags_DRM_GPUVM_RESV_PROTECTED, 14482b78182SAsahi Lina dev.as_raw(), 14582b78182SAsahi Lina r_obj.as_raw(), 14682b78182SAsahi Lina range.start, 14782b78182SAsahi Lina range.end - range.start, 14882b78182SAsahi Lina reserve_range.start, 14982b78182SAsahi Lina reserve_range.end - reserve_range.start, 15082b78182SAsahi Lina const { Self::vtable() }, 15182b78182SAsahi Lina ) 15282b78182SAsahi Lina } 15382b78182SAsahi Lina }), 15482b78182SAsahi Lina }? E), 15582b78182SAsahi Lina GFP_KERNEL | __GFP_ZERO, 15682b78182SAsahi Lina )?; 15782b78182SAsahi Lina // SAFETY: This transfers the initial refcount to the ARef. 15882b78182SAsahi Lina let aref = unsafe { 15982b78182SAsahi Lina ARef::from_raw(NonNull::new_unchecked(KBox::into_raw( 16082b78182SAsahi Lina Pin::into_inner_unchecked(obj), 16182b78182SAsahi Lina ))) 16282b78182SAsahi Lina }; 16382b78182SAsahi Lina // INVARIANT: This reference is unique. 16482b78182SAsahi Lina Ok(UniqueRefGpuVm(aref)) 16582b78182SAsahi Lina } 16682b78182SAsahi Lina 16782b78182SAsahi Lina /// Access this [`GpuVm`] from a raw pointer. 16882b78182SAsahi Lina /// 16982b78182SAsahi Lina /// # Safety 17082b78182SAsahi Lina /// 17182b78182SAsahi Lina /// The pointer must reference the `struct drm_gpuvm` in a valid [`GpuVm<T>`] that remains 17282b78182SAsahi Lina /// valid for at least `'a`. 17382b78182SAsahi Lina #[inline] 17482b78182SAsahi Lina pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm) -> &'a Self { 17582b78182SAsahi Lina // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`. Caller ensures the 17682b78182SAsahi Lina // pointer is valid for 'a. 17782b78182SAsahi Lina unsafe { &*kernel::container_of!(Opaque::cast_from(ptr), Self, vm) } 17882b78182SAsahi Lina } 17982b78182SAsahi Lina 18082b78182SAsahi Lina /// Returns a raw pointer to the embedded `struct drm_gpuvm`. 18182b78182SAsahi Lina #[inline] 18282b78182SAsahi Lina pub fn as_raw(&self) -> *mut bindings::drm_gpuvm { 18382b78182SAsahi Lina self.vm.get() 18482b78182SAsahi Lina } 18582b78182SAsahi Lina 18682b78182SAsahi Lina /// The start of the VA space. 18782b78182SAsahi Lina #[inline] 18882b78182SAsahi Lina pub fn va_start(&self) -> u64 { 18982b78182SAsahi Lina // SAFETY: The `mm_start` field is immutable. 19082b78182SAsahi Lina unsafe { (*self.as_raw()).mm_start } 19182b78182SAsahi Lina } 19282b78182SAsahi Lina 19382b78182SAsahi Lina /// The length of the GPU's virtual address space. 19482b78182SAsahi Lina #[inline] 19582b78182SAsahi Lina pub fn va_length(&self) -> u64 { 19682b78182SAsahi Lina // SAFETY: The `mm_range` field is immutable. 19782b78182SAsahi Lina unsafe { (*self.as_raw()).mm_range } 19882b78182SAsahi Lina } 19982b78182SAsahi Lina 20082b78182SAsahi Lina /// Returns the range of the GPU virtual address space. 20182b78182SAsahi Lina #[inline] 20282b78182SAsahi Lina pub fn va_range(&self) -> Range<u64> { 20382b78182SAsahi Lina let start = self.va_start(); 20482b78182SAsahi Lina // OVERFLOW: This reconstructs the Range<u64> passed to the constructor, so it won't fail. 20582b78182SAsahi Lina let end = start + self.va_length(); 20682b78182SAsahi Lina Range { start, end } 20782b78182SAsahi Lina } 20882b78182SAsahi Lina 20971a39211SAlice Ryhl /// Get or create the [`GpuVmBo`] for this gem object. 21071a39211SAlice Ryhl #[inline] 21171a39211SAlice Ryhl pub fn obtain( 21271a39211SAlice Ryhl &self, 21371a39211SAlice Ryhl obj: &T::Object, 21471a39211SAlice Ryhl data: impl PinInit<T::VmBoData>, 21571a39211SAlice Ryhl ) -> Result<ARef<GpuVmBo<T>>, AllocError> { 21671a39211SAlice Ryhl Ok(GpuVmBoAlloc::new(self, obj, data)?.obtain()) 21771a39211SAlice Ryhl } 21871a39211SAlice Ryhl 21982b78182SAsahi Lina /// Clean up buffer objects that are no longer used. 22082b78182SAsahi Lina #[inline] 22182b78182SAsahi Lina pub fn deferred_cleanup(&self) { 22282b78182SAsahi Lina // SAFETY: This GPUVM uses immediate mode. 22382b78182SAsahi Lina unsafe { bindings::drm_gpuvm_bo_deferred_cleanup(self.as_raw()) } 22482b78182SAsahi Lina } 22582b78182SAsahi Lina 22682b78182SAsahi Lina /// Check if this GEM object is an external object for this GPUVM. 22782b78182SAsahi Lina #[inline] 22882b78182SAsahi Lina pub fn is_extobj(&self, obj: &T::Object) -> bool { 22982b78182SAsahi Lina // SAFETY: We may call this with any GPUVM and GEM object. 23082b78182SAsahi Lina unsafe { bindings::drm_gpuvm_is_extobj(self.as_raw(), obj.as_raw()) } 23182b78182SAsahi Lina } 23282b78182SAsahi Lina 23382b78182SAsahi Lina /// Free this GPUVM. 23482b78182SAsahi Lina /// 23582b78182SAsahi Lina /// # Safety 23682b78182SAsahi Lina /// 23782b78182SAsahi Lina /// Called when refcount hits zero. 23882b78182SAsahi Lina unsafe extern "C" fn vm_free(me: *mut bindings::drm_gpuvm) { 23982b78182SAsahi Lina // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`. 24082b78182SAsahi Lina let me = unsafe { kernel::container_of!(Opaque::cast_from(me), Self, vm).cast_mut() }; 24182b78182SAsahi Lina // SAFETY: By type invariants we can free it when refcount hits zero. 24282b78182SAsahi Lina drop(unsafe { KBox::from_raw(me) }) 24382b78182SAsahi Lina } 24471a39211SAlice Ryhl 24571a39211SAlice Ryhl #[inline] 24671a39211SAlice Ryhl fn raw_resv(&self) -> *mut bindings::dma_resv { 24771a39211SAlice Ryhl // SAFETY: `r_obj` is immutable and valid for duration of GPUVM. 24871a39211SAlice Ryhl unsafe { (*(*self.as_raw()).r_obj).resv } 24971a39211SAlice Ryhl } 25082b78182SAsahi Lina } 25182b78182SAsahi Lina 25282b78182SAsahi Lina /// The manager for a GPUVM. 25382b78182SAsahi Lina pub trait DriverGpuVm: Sized + Send { 25482b78182SAsahi Lina /// Parent `Driver` for this object. 25582b78182SAsahi Lina type Driver: drm::Driver<Object = Self::Object>; 25682b78182SAsahi Lina 25782b78182SAsahi Lina /// The kind of GEM object stored in this GPUVM. 25882b78182SAsahi Lina type Object: IntoGEMObject; 25971a39211SAlice Ryhl 2605540a9c7SAlice Ryhl /// Data stored with each [`struct drm_gpuva`](struct@GpuVa). 2615540a9c7SAlice Ryhl type VaData; 2625540a9c7SAlice Ryhl 26371a39211SAlice Ryhl /// Data stored with each [`struct drm_gpuvm_bo`](struct@GpuVmBo). 26471a39211SAlice Ryhl type VmBoData; 265*dc384604SAlice Ryhl 266*dc384604SAlice Ryhl /// The private data passed to callbacks. 267*dc384604SAlice Ryhl type SmContext<'ctx>; 268*dc384604SAlice Ryhl 269*dc384604SAlice Ryhl /// Indicates that an existing mapping should be removed. 270*dc384604SAlice Ryhl fn sm_step_unmap<'op, 'ctx>( 271*dc384604SAlice Ryhl &mut self, 272*dc384604SAlice Ryhl op: OpUnmap<'op, Self>, 273*dc384604SAlice Ryhl context: &mut Self::SmContext<'ctx>, 274*dc384604SAlice Ryhl ) -> Result<OpUnmapped<'op, Self>, Error>; 275*dc384604SAlice Ryhl 276*dc384604SAlice Ryhl /// Indicates that an existing mapping should be split up. 277*dc384604SAlice Ryhl fn sm_step_remap<'op, 'ctx>( 278*dc384604SAlice Ryhl &mut self, 279*dc384604SAlice Ryhl op: OpRemap<'op, Self>, 280*dc384604SAlice Ryhl context: &mut Self::SmContext<'ctx>, 281*dc384604SAlice Ryhl ) -> Result<OpRemapped<'op, Self>, Error>; 28282b78182SAsahi Lina } 28382b78182SAsahi Lina 28482b78182SAsahi Lina /// The core of the DRM GPU VA manager. 28582b78182SAsahi Lina /// 28682b78182SAsahi Lina /// This object is a unique reference to the VM that can access the interval tree and the Rust 28782b78182SAsahi Lina /// `data` field. 28882b78182SAsahi Lina /// 28982b78182SAsahi Lina /// # Invariants 29082b78182SAsahi Lina /// 29182b78182SAsahi Lina /// Each `GpuVm` instance has at most one `UniqueRefGpuVm` reference. 29282b78182SAsahi Lina pub struct UniqueRefGpuVm<T: DriverGpuVm>(ARef<GpuVm<T>>); 29382b78182SAsahi Lina 29482b78182SAsahi Lina // SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel, and 29582b78182SAsahi Lina // concurrent access to `data` is safe due to the `T: Sync` requirement. 29682b78182SAsahi Lina unsafe impl<T: DriverGpuVm + Sync> Sync for UniqueRefGpuVm<T> {} 29782b78182SAsahi Lina 29882b78182SAsahi Lina impl<T: DriverGpuVm> UniqueRefGpuVm<T> { 29982b78182SAsahi Lina /// Access the data owned by this `UniqueRefGpuVm` immutably. 30082b78182SAsahi Lina #[inline] 30182b78182SAsahi Lina pub fn data_ref(&self) -> &T { 30282b78182SAsahi Lina // SAFETY: By the type invariants we may access `data`. 30382b78182SAsahi Lina unsafe { &*self.0.data.get() } 30482b78182SAsahi Lina } 30582b78182SAsahi Lina 30682b78182SAsahi Lina /// Access the data owned by this `UniqueRefGpuVm` mutably. 30782b78182SAsahi Lina #[inline] 30882b78182SAsahi Lina pub fn data(&mut self) -> &mut T { 30982b78182SAsahi Lina // SAFETY: By the type invariants we may access `data`. 31082b78182SAsahi Lina unsafe { &mut *self.0.data.get() } 31182b78182SAsahi Lina } 31282b78182SAsahi Lina } 31382b78182SAsahi Lina 31482b78182SAsahi Lina impl<T: DriverGpuVm> Deref for UniqueRefGpuVm<T> { 31582b78182SAsahi Lina type Target = GpuVm<T>; 31682b78182SAsahi Lina 31782b78182SAsahi Lina #[inline] 31882b78182SAsahi Lina fn deref(&self) -> &GpuVm<T> { 31982b78182SAsahi Lina &self.0 32082b78182SAsahi Lina } 32182b78182SAsahi Lina } 322