kernel/drm/gpuvm/va.rs
1// SPDX-License-Identifier: GPL-2.0 OR MIT
2
3use 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]
17pub struct GpuVa<T: DriverGpuVm> {
18 #[pin]
19 inner: Opaque<bindings::drm_gpuva>,
20 #[pin]
21 data: T::VaData,
22}
23
24impl<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}
30impl<T: DriverGpuVm> Eq for GpuVa<T> {}
31
32impl<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.
105pub struct GpuVaAlloc<T: DriverGpuVm>(KBox<MaybeUninit<GpuVa<T>>>);
106
107// SAFETY: A `GpuVaAlloc` is an owned, uninitialised allocation with no live `T::VaData` and no
108// thread-bound state.
109unsafe impl<T: DriverGpuVm> Send for GpuVaAlloc<T> {}
110
111// SAFETY: A `GpuVaAlloc` has no `&self` method that reaches its contents, so a shared
112// `&GpuVaAlloc` cannot access the allocation.
113unsafe impl<T: DriverGpuVm> Sync for GpuVaAlloc<T> {}
114
115impl<T: DriverGpuVm> GpuVaAlloc<T> {
116 /// Pre-allocate a [`GpuVa`] object.
117 pub fn new(flags: AllocFlags) -> Result<GpuVaAlloc<T>, AllocError> {
118 // INVARIANTS: Memory allocated with __GFP_ZERO.
119 Ok(GpuVaAlloc(KBox::new_uninit(flags | __GFP_ZERO)?))
120 }
121
122 /// Prepare this `drm_gpuva` for insertion into the GPUVM.
123 #[must_use]
124 pub(super) fn prepare(mut self, va_data: impl PinInit<T::VaData>) -> *mut bindings::drm_gpuva {
125 let va_ptr = MaybeUninit::as_mut_ptr(&mut self.0);
126 // SAFETY: The `data` field is pinned.
127 unsafe { pin_init::raw_init(&raw mut (*va_ptr).data, va_data) };
128 KBox::into_raw(self.0).cast()
129 }
130}
131
132/// A [`GpuVa`] object that has been removed.
133///
134/// # Invariants
135///
136/// The `drm_gpuva` is not resident in the [`GpuVm`].
137pub struct GpuVaRemoved<T: DriverGpuVm>(KBox<GpuVa<T>>);
138
139impl<T: DriverGpuVm> GpuVaRemoved<T> {
140 /// Convert a raw pointer into a [`GpuVaRemoved`].
141 ///
142 /// # Safety
143 ///
144 /// * Must have been removed from a [`GpuVm<T>`].
145 /// * It must not be a `gpuvm->kernel_alloc_node` va.
146 pub(super) unsafe fn from_raw(ptr: *mut bindings::drm_gpuva) -> Self {
147 // SAFETY: Since it used to be a VA in a `GpuVm<T>` and it's not a kernel_alloc_node, this
148 // pointer references a `GpuVa<T>` with a valid `T::VaData`. Since it has been removed, we
149 // can take ownership of the allocation.
150 GpuVaRemoved(unsafe { KBox::from_raw(ptr.cast()) })
151 }
152
153 /// Take ownership of the VA data.
154 pub fn into_inner(self) -> T::VaData
155 where
156 T::VaData: Unpin,
157 {
158 KBox::into_inner(self.0).data
159 }
160}
161
162impl<T: DriverGpuVm> Deref for GpuVaRemoved<T> {
163 type Target = T::VaData;
164 fn deref(&self) -> &T::VaData {
165 &self.0.data
166 }
167}
168
169impl<T: DriverGpuVm> DerefMut for GpuVaRemoved<T>
170where
171 T::VaData: Unpin,
172{
173 fn deref_mut(&mut self) -> &mut T::VaData {
174 &mut self.0.data
175 }
176}