Skip to main content

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}