1 // SPDX-License-Identifier: GPL-2.0 OR MIT 2 3 use super::*; 4 5 /// Represents that a range of a GEM object is mapped in this [`GpuVm`] instance. 6 /// 7 /// Does not assume that GEM lock is held. 8 /// 9 /// # Invariants 10 /// 11 /// * This is a valid `drm_gpuva` object that is resident in a [`GpuVm<T>`] instance. 12 /// * It is associated with a [`GpuVmBo<T>`]. Or in other words, it's not an 13 /// `gpuvm->kernel_alloc_node` and `DRM_GPUVA_SPARSE` is not set. 14 /// * The associated [`GpuVmBo<T>`] is part of the GEM list. 15 #[repr(C)] 16 #[pin_data] 17 pub struct GpuVa<T: DriverGpuVm> { 18 #[pin] 19 inner: Opaque<bindings::drm_gpuva>, 20 #[pin] 21 data: T::VaData, 22 } 23 24 impl<T: DriverGpuVm> PartialEq for GpuVa<T> { 25 #[inline] 26 fn eq(&self, other: &Self) -> bool { 27 core::ptr::eq(self.as_raw(), other.as_raw()) 28 } 29 } 30 impl<T: DriverGpuVm> Eq for GpuVa<T> {} 31 32 impl<T: DriverGpuVm> GpuVa<T> { 33 /// Access this [`GpuVa`] from a raw pointer. 34 /// 35 /// # Safety 36 /// 37 /// * For the duration of `'a`, the pointer must reference a valid `drm_gpuva` associated with 38 /// a [`GpuVm<T>`]. 39 /// * It must be associated with a [`GpuVmBo<T>`]. 40 /// * The associated [`GpuVmBo<T>`] is part of the GEM list. 41 #[inline] 42 pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuva) -> &'a Self { 43 // CAST: `drm_gpuva` is first field and `repr(C)`. 44 // SAFETY: The safety requirements match the invariants of `GpuVa`. 45 unsafe { &*ptr.cast() } 46 } 47 48 /// Returns a raw pointer to underlying C value. 49 #[inline] 50 pub fn as_raw(&self) -> *mut bindings::drm_gpuva { 51 self.inner.get() 52 } 53 54 /// Returns the address of this mapping in the GPU virtual address space. 55 #[inline] 56 pub fn addr(&self) -> u64 { 57 // SAFETY: The `va.addr` field of `drm_gpuva` is immutable. 58 unsafe { (*self.as_raw()).va.addr } 59 } 60 61 /// Returns the length of this mapping. 62 #[inline] 63 pub fn length(&self) -> u64 { 64 // SAFETY: The `va.range` field of `drm_gpuva` is immutable. 65 unsafe { (*self.as_raw()).va.range } 66 } 67 68 /// Returns `addr..addr+length`. 69 #[inline] 70 pub fn range(&self) -> Range<u64> { 71 let addr = self.addr(); 72 addr..addr + self.length() 73 } 74 75 /// Returns the offset within the GEM object. 76 #[inline] 77 pub fn gem_offset(&self) -> u64 { 78 // SAFETY: The `gem.offset` field of `drm_gpuva` is immutable. 79 unsafe { (*self.as_raw()).gem.offset } 80 } 81 82 /// Returns the GEM object. 83 #[inline] 84 pub fn obj(&self) -> &T::Object { 85 // SAFETY: The `gem.obj` field of `drm_gpuva` is immutable. We know that it's not null 86 // because this VA is associated with a `GpuVmBo<T>`. 87 unsafe { <T::Object as IntoGEMObject>::from_raw((*self.as_raw()).gem.obj) } 88 } 89 90 /// Returns the underlying [`GpuVmBo`] object that backs this [`GpuVa`]. 91 #[inline] 92 pub fn vm_bo(&self) -> &GpuVmBo<T> { 93 // SAFETY: The `vm_bo` field of `drm_gpuva` is immutable. We know that it's not null 94 // because this VA is associated with a `GpuVmBo<T>`. The BO is in the GEM list by the type 95 // invariants. 96 unsafe { GpuVmBo::from_raw((*self.as_raw()).vm_bo) } 97 } 98 } 99 100 /// A pre-allocated [`GpuVa`] object. 101 /// 102 /// # Invariants 103 /// 104 /// The memory is zeroed. 105 pub struct GpuVaAlloc<T: DriverGpuVm>(KBox<MaybeUninit<GpuVa<T>>>); 106 107 impl<T: DriverGpuVm> GpuVaAlloc<T> { 108 /// Pre-allocate a [`GpuVa`] object. 109 pub fn new(flags: AllocFlags) -> Result<GpuVaAlloc<T>, AllocError> { 110 // INVARIANTS: Memory allocated with __GFP_ZERO. 111 Ok(GpuVaAlloc(KBox::new_uninit(flags | __GFP_ZERO)?)) 112 } 113 114 /// Prepare this `drm_gpuva` for insertion into the GPUVM. 115 #[must_use] 116 pub(super) fn prepare(mut self, va_data: impl PinInit<T::VaData>) -> *mut bindings::drm_gpuva { 117 let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0); 118 // SAFETY: The `data` field is pinned. 119 let Ok(()) = unsafe { va_data.__pinned_init(&raw mut (*va_ptr).data) }; 120 KBox::into_raw(self.0).cast() 121 } 122 } 123 124 /// A [`GpuVa`] object that has been removed. 125 /// 126 /// # Invariants 127 /// 128 /// The `drm_gpuva` is not resident in the [`GpuVm`]. 129 pub struct GpuVaRemoved<T: DriverGpuVm>(KBox<GpuVa<T>>); 130 131 impl<T: DriverGpuVm> GpuVaRemoved<T> { 132 /// Convert a raw pointer into a [`GpuVaRemoved`]. 133 /// 134 /// # Safety 135 /// 136 /// * Must have been removed from a [`GpuVm<T>`]. 137 /// * It must not be a `gpuvm->kernel_alloc_node` va. 138 pub(super) unsafe fn from_raw(ptr: *mut bindings::drm_gpuva) -> Self { 139 // SAFETY: Since it used to be a VA in a `GpuVm<T>` and it's not a kernel_alloc_node, this 140 // pointer references a `GpuVa<T>` with a valid `T::VaData`. Since it has been removed, we 141 // can take ownership of the allocation. 142 GpuVaRemoved(unsafe { KBox::from_raw(ptr.cast()) }) 143 } 144 145 /// Take ownership of the VA data. 146 pub fn into_inner(self) -> T::VaData 147 where 148 T::VaData: Unpin, 149 { 150 KBox::into_inner(self.0).data 151 } 152 } 153 154 impl<T: DriverGpuVm> Deref for GpuVaRemoved<T> { 155 type Target = T::VaData; 156 fn deref(&self) -> &T::VaData { 157 &self.0.data 158 } 159 } 160 161 impl<T: DriverGpuVm> DerefMut for GpuVaRemoved<T> 162 where 163 T::VaData: Unpin, 164 { 165 fn deref_mut(&mut self) -> &mut T::VaData { 166 &mut self.0.data 167 } 168 } 169