xref: /linux/rust/kernel/drm/gpuvm/mod.rs (revision 5540a9c747b3e1914ae68ae89f71c0a779bee5d1)
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::{
14*5540a9c7SAlice Ryhl     alloc::{
15*5540a9c7SAlice Ryhl         AllocError,
16*5540a9c7SAlice Ryhl         Flags as AllocFlags, //
17*5540a9c7SAlice Ryhl     },
1882b78182SAsahi Lina     bindings,
1982b78182SAsahi Lina     drm,
2082b78182SAsahi Lina     drm::gem::IntoGEMObject,
2182b78182SAsahi Lina     prelude::*,
2282b78182SAsahi Lina     sync::aref::{
2382b78182SAsahi Lina         ARef,
2482b78182SAsahi Lina         AlwaysRefCounted, //
2582b78182SAsahi Lina     },
2682b78182SAsahi Lina     types::Opaque, //
2782b78182SAsahi Lina };
2882b78182SAsahi Lina 
2982b78182SAsahi Lina use core::{
3082b78182SAsahi Lina     cell::UnsafeCell,
31*5540a9c7SAlice Ryhl     mem::{
32*5540a9c7SAlice Ryhl         ManuallyDrop,
33*5540a9c7SAlice Ryhl         MaybeUninit, //
34*5540a9c7SAlice Ryhl     },
3582b78182SAsahi Lina     ops::{
3682b78182SAsahi Lina         Deref,
37*5540a9c7SAlice Ryhl         DerefMut,
3882b78182SAsahi Lina         Range, //
3982b78182SAsahi Lina     },
4071a39211SAlice Ryhl     ptr::{
4171a39211SAlice Ryhl         self,
4271a39211SAlice Ryhl         NonNull, //
4371a39211SAlice Ryhl     }, //
4482b78182SAsahi Lina };
4582b78182SAsahi Lina 
46*5540a9c7SAlice Ryhl mod va;
47*5540a9c7SAlice Ryhl pub use self::va::*;
48*5540a9c7SAlice Ryhl 
4971a39211SAlice Ryhl mod vm_bo;
5071a39211SAlice Ryhl pub use self::vm_bo::*;
5171a39211SAlice Ryhl 
5282b78182SAsahi Lina /// A DRM GPU VA manager.
5382b78182SAsahi Lina ///
5482b78182SAsahi Lina /// This object is refcounted, but the locations of mapped ranges may only be accessed or changed
5582b78182SAsahi Lina /// via the special unique handle [`UniqueRefGpuVm`].
5682b78182SAsahi Lina ///
5782b78182SAsahi Lina /// # Invariants
5882b78182SAsahi Lina ///
5982b78182SAsahi Lina /// * Stored in an allocation managed by the refcount in `self.vm`.
6082b78182SAsahi Lina /// * Access to `data` and the gpuvm interval tree is controlled via the [`UniqueRefGpuVm`] type.
61*5540a9c7SAlice Ryhl /// * Does not contain any sparse [`GpuVa<T>`] instances.
6282b78182SAsahi Lina #[pin_data]
6382b78182SAsahi Lina pub struct GpuVm<T: DriverGpuVm> {
6482b78182SAsahi Lina     #[pin]
6582b78182SAsahi Lina     vm: Opaque<bindings::drm_gpuvm>,
6682b78182SAsahi Lina     /// Accessed only through the [`UniqueRefGpuVm`] reference.
6782b78182SAsahi Lina     data: UnsafeCell<T>,
6882b78182SAsahi Lina }
6982b78182SAsahi Lina 
7082b78182SAsahi Lina // SAFETY: The GPUVM api does not assume that it is tied to a specific thread. The destructor will
7182b78182SAsahi Lina // drop the `data` field, which is okay because it is guaranteed `Send` by the `DriverGpuVm` trait.
7282b78182SAsahi Lina unsafe impl<T: DriverGpuVm> Send for GpuVm<T> {}
7382b78182SAsahi Lina // SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel.
7482b78182SAsahi Lina unsafe impl<T: DriverGpuVm> Sync for GpuVm<T> {}
7582b78182SAsahi Lina 
7682b78182SAsahi Lina // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
7782b78182SAsahi Lina unsafe impl<T: DriverGpuVm> AlwaysRefCounted for GpuVm<T> {
7882b78182SAsahi Lina     fn inc_ref(&self) {
7982b78182SAsahi Lina         // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
8082b78182SAsahi Lina         unsafe { bindings::drm_gpuvm_get(self.vm.get()) };
8182b78182SAsahi Lina     }
8282b78182SAsahi Lina 
8382b78182SAsahi Lina     unsafe fn dec_ref(obj: NonNull<Self>) {
8482b78182SAsahi Lina         // SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
8582b78182SAsahi Lina         unsafe { bindings::drm_gpuvm_put((*obj.as_ptr()).vm.get()) };
8682b78182SAsahi Lina     }
8782b78182SAsahi Lina }
8882b78182SAsahi Lina 
8982b78182SAsahi Lina impl<T: DriverGpuVm> PartialEq for GpuVm<T> {
9082b78182SAsahi Lina     #[inline]
9182b78182SAsahi Lina     fn eq(&self, other: &Self) -> bool {
9282b78182SAsahi Lina         core::ptr::eq(self.as_raw(), other.as_raw())
9382b78182SAsahi Lina     }
9482b78182SAsahi Lina }
9582b78182SAsahi Lina impl<T: DriverGpuVm> Eq for GpuVm<T> {}
9682b78182SAsahi Lina 
9782b78182SAsahi Lina impl<T: DriverGpuVm> GpuVm<T> {
9882b78182SAsahi Lina     const fn vtable() -> &'static bindings::drm_gpuvm_ops {
9982b78182SAsahi Lina         &bindings::drm_gpuvm_ops {
10082b78182SAsahi Lina             vm_free: Some(Self::vm_free),
10182b78182SAsahi Lina             op_alloc: None,
10282b78182SAsahi Lina             op_free: None,
10371a39211SAlice Ryhl             vm_bo_alloc: GpuVmBo::<T>::ALLOC_FN,
10471a39211SAlice Ryhl             vm_bo_free: GpuVmBo::<T>::FREE_FN,
10582b78182SAsahi Lina             vm_bo_validate: None,
10682b78182SAsahi Lina             sm_step_map: None,
10782b78182SAsahi Lina             sm_step_unmap: None,
10882b78182SAsahi Lina             sm_step_remap: None,
10982b78182SAsahi Lina         }
11082b78182SAsahi Lina     }
11182b78182SAsahi Lina 
11282b78182SAsahi Lina     /// Creates a GPUVM instance.
11382b78182SAsahi Lina     #[expect(clippy::new_ret_no_self)]
11482b78182SAsahi Lina     pub fn new<E>(
11582b78182SAsahi Lina         name: &'static CStr,
11682b78182SAsahi Lina         dev: &drm::Device<T::Driver>,
11782b78182SAsahi Lina         r_obj: &T::Object,
11882b78182SAsahi Lina         range: Range<u64>,
11982b78182SAsahi Lina         reserve_range: Range<u64>,
12082b78182SAsahi Lina         data: T,
12182b78182SAsahi Lina     ) -> Result<UniqueRefGpuVm<T>, E>
12282b78182SAsahi Lina     where
12382b78182SAsahi Lina         E: From<AllocError>,
12482b78182SAsahi Lina         E: From<core::convert::Infallible>,
12582b78182SAsahi Lina     {
12682b78182SAsahi Lina         let obj = KBox::try_pin_init::<E>(
12782b78182SAsahi Lina             try_pin_init!(Self {
12882b78182SAsahi Lina                 data: UnsafeCell::new(data),
12982b78182SAsahi Lina                 vm <- Opaque::ffi_init(|vm| {
13082b78182SAsahi Lina                     // SAFETY: These arguments are valid. `vm` is valid until refcount drops to
13182b78182SAsahi Lina                     // zero. The `vm` is zeroed before calling this method by `__GFP_ZERO` flag
13282b78182SAsahi Lina                     // below.
13382b78182SAsahi Lina                     unsafe {
13482b78182SAsahi Lina                         bindings::drm_gpuvm_init(
13582b78182SAsahi Lina                             vm,
13682b78182SAsahi Lina                             name.as_char_ptr(),
13782b78182SAsahi Lina                             bindings::drm_gpuvm_flags_DRM_GPUVM_IMMEDIATE_MODE
13882b78182SAsahi Lina                                 | bindings::drm_gpuvm_flags_DRM_GPUVM_RESV_PROTECTED,
13982b78182SAsahi Lina                             dev.as_raw(),
14082b78182SAsahi Lina                             r_obj.as_raw(),
14182b78182SAsahi Lina                             range.start,
14282b78182SAsahi Lina                             range.end - range.start,
14382b78182SAsahi Lina                             reserve_range.start,
14482b78182SAsahi Lina                             reserve_range.end - reserve_range.start,
14582b78182SAsahi Lina                             const { Self::vtable() },
14682b78182SAsahi Lina                         )
14782b78182SAsahi Lina                     }
14882b78182SAsahi Lina                 }),
14982b78182SAsahi Lina             }? E),
15082b78182SAsahi Lina             GFP_KERNEL | __GFP_ZERO,
15182b78182SAsahi Lina         )?;
15282b78182SAsahi Lina         // SAFETY: This transfers the initial refcount to the ARef.
15382b78182SAsahi Lina         let aref = unsafe {
15482b78182SAsahi Lina             ARef::from_raw(NonNull::new_unchecked(KBox::into_raw(
15582b78182SAsahi Lina                 Pin::into_inner_unchecked(obj),
15682b78182SAsahi Lina             )))
15782b78182SAsahi Lina         };
15882b78182SAsahi Lina         // INVARIANT: This reference is unique.
15982b78182SAsahi Lina         Ok(UniqueRefGpuVm(aref))
16082b78182SAsahi Lina     }
16182b78182SAsahi Lina 
16282b78182SAsahi Lina     /// Access this [`GpuVm`] from a raw pointer.
16382b78182SAsahi Lina     ///
16482b78182SAsahi Lina     /// # Safety
16582b78182SAsahi Lina     ///
16682b78182SAsahi Lina     /// The pointer must reference the `struct drm_gpuvm` in a valid [`GpuVm<T>`] that remains
16782b78182SAsahi Lina     /// valid for at least `'a`.
16882b78182SAsahi Lina     #[inline]
16982b78182SAsahi Lina     pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm) -> &'a Self {
17082b78182SAsahi Lina         // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`. Caller ensures the
17182b78182SAsahi Lina         // pointer is valid for 'a.
17282b78182SAsahi Lina         unsafe { &*kernel::container_of!(Opaque::cast_from(ptr), Self, vm) }
17382b78182SAsahi Lina     }
17482b78182SAsahi Lina 
17582b78182SAsahi Lina     /// Returns a raw pointer to the embedded `struct drm_gpuvm`.
17682b78182SAsahi Lina     #[inline]
17782b78182SAsahi Lina     pub fn as_raw(&self) -> *mut bindings::drm_gpuvm {
17882b78182SAsahi Lina         self.vm.get()
17982b78182SAsahi Lina     }
18082b78182SAsahi Lina 
18182b78182SAsahi Lina     /// The start of the VA space.
18282b78182SAsahi Lina     #[inline]
18382b78182SAsahi Lina     pub fn va_start(&self) -> u64 {
18482b78182SAsahi Lina         // SAFETY: The `mm_start` field is immutable.
18582b78182SAsahi Lina         unsafe { (*self.as_raw()).mm_start }
18682b78182SAsahi Lina     }
18782b78182SAsahi Lina 
18882b78182SAsahi Lina     /// The length of the GPU's virtual address space.
18982b78182SAsahi Lina     #[inline]
19082b78182SAsahi Lina     pub fn va_length(&self) -> u64 {
19182b78182SAsahi Lina         // SAFETY: The `mm_range` field is immutable.
19282b78182SAsahi Lina         unsafe { (*self.as_raw()).mm_range }
19382b78182SAsahi Lina     }
19482b78182SAsahi Lina 
19582b78182SAsahi Lina     /// Returns the range of the GPU virtual address space.
19682b78182SAsahi Lina     #[inline]
19782b78182SAsahi Lina     pub fn va_range(&self) -> Range<u64> {
19882b78182SAsahi Lina         let start = self.va_start();
19982b78182SAsahi Lina         // OVERFLOW: This reconstructs the Range<u64> passed to the constructor, so it won't fail.
20082b78182SAsahi Lina         let end = start + self.va_length();
20182b78182SAsahi Lina         Range { start, end }
20282b78182SAsahi Lina     }
20382b78182SAsahi Lina 
20471a39211SAlice Ryhl     /// Get or create the [`GpuVmBo`] for this gem object.
20571a39211SAlice Ryhl     #[inline]
20671a39211SAlice Ryhl     pub fn obtain(
20771a39211SAlice Ryhl         &self,
20871a39211SAlice Ryhl         obj: &T::Object,
20971a39211SAlice Ryhl         data: impl PinInit<T::VmBoData>,
21071a39211SAlice Ryhl     ) -> Result<ARef<GpuVmBo<T>>, AllocError> {
21171a39211SAlice Ryhl         Ok(GpuVmBoAlloc::new(self, obj, data)?.obtain())
21271a39211SAlice Ryhl     }
21371a39211SAlice Ryhl 
21482b78182SAsahi Lina     /// Clean up buffer objects that are no longer used.
21582b78182SAsahi Lina     #[inline]
21682b78182SAsahi Lina     pub fn deferred_cleanup(&self) {
21782b78182SAsahi Lina         // SAFETY: This GPUVM uses immediate mode.
21882b78182SAsahi Lina         unsafe { bindings::drm_gpuvm_bo_deferred_cleanup(self.as_raw()) }
21982b78182SAsahi Lina     }
22082b78182SAsahi Lina 
22182b78182SAsahi Lina     /// Check if this GEM object is an external object for this GPUVM.
22282b78182SAsahi Lina     #[inline]
22382b78182SAsahi Lina     pub fn is_extobj(&self, obj: &T::Object) -> bool {
22482b78182SAsahi Lina         // SAFETY: We may call this with any GPUVM and GEM object.
22582b78182SAsahi Lina         unsafe { bindings::drm_gpuvm_is_extobj(self.as_raw(), obj.as_raw()) }
22682b78182SAsahi Lina     }
22782b78182SAsahi Lina 
22882b78182SAsahi Lina     /// Free this GPUVM.
22982b78182SAsahi Lina     ///
23082b78182SAsahi Lina     /// # Safety
23182b78182SAsahi Lina     ///
23282b78182SAsahi Lina     /// Called when refcount hits zero.
23382b78182SAsahi Lina     unsafe extern "C" fn vm_free(me: *mut bindings::drm_gpuvm) {
23482b78182SAsahi Lina         // SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`.
23582b78182SAsahi Lina         let me = unsafe { kernel::container_of!(Opaque::cast_from(me), Self, vm).cast_mut() };
23682b78182SAsahi Lina         // SAFETY: By type invariants we can free it when refcount hits zero.
23782b78182SAsahi Lina         drop(unsafe { KBox::from_raw(me) })
23882b78182SAsahi Lina     }
23971a39211SAlice Ryhl 
24071a39211SAlice Ryhl     #[inline]
24171a39211SAlice Ryhl     fn raw_resv(&self) -> *mut bindings::dma_resv {
24271a39211SAlice Ryhl         // SAFETY: `r_obj` is immutable and valid for duration of GPUVM.
24371a39211SAlice Ryhl         unsafe { (*(*self.as_raw()).r_obj).resv }
24471a39211SAlice Ryhl     }
24582b78182SAsahi Lina }
24682b78182SAsahi Lina 
24782b78182SAsahi Lina /// The manager for a GPUVM.
24882b78182SAsahi Lina pub trait DriverGpuVm: Sized + Send {
24982b78182SAsahi Lina     /// Parent `Driver` for this object.
25082b78182SAsahi Lina     type Driver: drm::Driver<Object = Self::Object>;
25182b78182SAsahi Lina 
25282b78182SAsahi Lina     /// The kind of GEM object stored in this GPUVM.
25382b78182SAsahi Lina     type Object: IntoGEMObject;
25471a39211SAlice Ryhl 
255*5540a9c7SAlice Ryhl     /// Data stored with each [`struct drm_gpuva`](struct@GpuVa).
256*5540a9c7SAlice Ryhl     type VaData;
257*5540a9c7SAlice Ryhl 
25871a39211SAlice Ryhl     /// Data stored with each [`struct drm_gpuvm_bo`](struct@GpuVmBo).
25971a39211SAlice Ryhl     type VmBoData;
26082b78182SAsahi Lina }
26182b78182SAsahi Lina 
26282b78182SAsahi Lina /// The core of the DRM GPU VA manager.
26382b78182SAsahi Lina ///
26482b78182SAsahi Lina /// This object is a unique reference to the VM that can access the interval tree and the Rust
26582b78182SAsahi Lina /// `data` field.
26682b78182SAsahi Lina ///
26782b78182SAsahi Lina /// # Invariants
26882b78182SAsahi Lina ///
26982b78182SAsahi Lina /// Each `GpuVm` instance has at most one `UniqueRefGpuVm` reference.
27082b78182SAsahi Lina pub struct UniqueRefGpuVm<T: DriverGpuVm>(ARef<GpuVm<T>>);
27182b78182SAsahi Lina 
27282b78182SAsahi Lina // SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel, and
27382b78182SAsahi Lina // concurrent access to `data` is safe due to the `T: Sync` requirement.
27482b78182SAsahi Lina unsafe impl<T: DriverGpuVm + Sync> Sync for UniqueRefGpuVm<T> {}
27582b78182SAsahi Lina 
27682b78182SAsahi Lina impl<T: DriverGpuVm> UniqueRefGpuVm<T> {
27782b78182SAsahi Lina     /// Access the data owned by this `UniqueRefGpuVm` immutably.
27882b78182SAsahi Lina     #[inline]
27982b78182SAsahi Lina     pub fn data_ref(&self) -> &T {
28082b78182SAsahi Lina         // SAFETY: By the type invariants we may access `data`.
28182b78182SAsahi Lina         unsafe { &*self.0.data.get() }
28282b78182SAsahi Lina     }
28382b78182SAsahi Lina 
28482b78182SAsahi Lina     /// Access the data owned by this `UniqueRefGpuVm` mutably.
28582b78182SAsahi Lina     #[inline]
28682b78182SAsahi Lina     pub fn data(&mut self) -> &mut T {
28782b78182SAsahi Lina         // SAFETY: By the type invariants we may access `data`.
28882b78182SAsahi Lina         unsafe { &mut *self.0.data.get() }
28982b78182SAsahi Lina     }
29082b78182SAsahi Lina }
29182b78182SAsahi Lina 
29282b78182SAsahi Lina impl<T: DriverGpuVm> Deref for UniqueRefGpuVm<T> {
29382b78182SAsahi Lina     type Target = GpuVm<T>;
29482b78182SAsahi Lina 
29582b78182SAsahi Lina     #[inline]
29682b78182SAsahi Lina     fn deref(&self) -> &GpuVm<T> {
29782b78182SAsahi Lina         &self.0
29882b78182SAsahi Lina     }
29982b78182SAsahi Lina }
300