Skip to main content

kernel/drm/gpuvm/
sm_ops.rs

1// SPDX-License-Identifier: GPL-2.0 OR MIT
2
3use super::*;
4
5/// The actual data that gets threaded through the callbacks.
6struct SmData<'a, 'ctx, T: DriverGpuVm + 'ctx> {
7    gpuvm: &'a mut UniqueRefGpuVm<T>,
8    user_context: &'a mut T::SmContext<'ctx>,
9}
10
11/// Adds an extra field to `SmData` for `sm_map()` callbacks.
12///
13/// # Invariants
14///
15/// `self.vm_bo.gpuvm() == self.sm_data.gpuvm`.
16#[repr(C)]
17struct SmMapData<'a, 'ctx, T: DriverGpuVm> {
18    sm_data: SmData<'a, 'ctx, T>,
19    vm_bo: &'a GpuVmBo<T>,
20}
21
22/// The argument for [`UniqueRefGpuVm::sm_map`].
23pub struct OpMapRequest<'a, 'ctx, T: DriverGpuVm + 'ctx> {
24    /// Address in GPU virtual address space.
25    pub addr: u64,
26    /// Length of mapping to create.
27    pub range: u64,
28    /// Offset in GEM object.
29    pub gem_offset: u64,
30    /// The GEM object to map.
31    pub vm_bo: &'a GpuVmBo<T>,
32    /// The user-provided context type.
33    pub context: &'a mut T::SmContext<'ctx>,
34}
35
36impl<'a, 'ctx, T: DriverGpuVm> OpMapRequest<'a, 'ctx, T> {
37    fn raw_request(&self) -> bindings::drm_gpuvm_map_req {
38        bindings::drm_gpuvm_map_req {
39            map: bindings::drm_gpuva_op_map {
40                va: bindings::drm_gpuva_op_map__bindgen_ty_1 {
41                    addr: self.addr,
42                    range: self.range,
43                },
44                gem: bindings::drm_gpuva_op_map__bindgen_ty_2 {
45                    offset: self.gem_offset,
46                    obj: self.vm_bo.obj().as_raw(),
47                },
48            },
49        }
50    }
51}
52
53/// Represents an `sm_step_map` operation that has not yet been completed.
54pub struct OpMap<'op, T: DriverGpuVm> {
55    op: &'op bindings::drm_gpuva_op_map,
56    // Since these abstractions are designed for immediate mode, the VM BO needs to be
57    // pre-allocated, so we always have it available when we reach this point.
58    vm_bo: &'op GpuVmBo<T>,
59    // This ensures that 'op is invariant, so that `OpMap<'long, T>` does not
60    // coerce to `OpMap<'short, T>`. This ensures that the user can't return
61    // the wrong `OpMapped` value.
62    _invariant: PhantomData<*mut &'op mut T>,
63}
64
65impl<'op, T: DriverGpuVm> OpMap<'op, T> {
66    /// The base address of the new mapping.
67    pub fn addr(&self) -> u64 {
68        self.op.va.addr
69    }
70
71    /// The length of the new mapping.
72    pub fn length(&self) -> u64 {
73        self.op.va.range
74    }
75
76    /// The offset within the [`drm_gem_object`](DriverGpuVm::Object).
77    pub fn gem_offset(&self) -> u64 {
78        self.op.gem.offset
79    }
80
81    /// The [`drm_gem_object`](DriverGpuVm::Object) to map.
82    pub fn obj(&self) -> &T::Object {
83        // SAFETY: The `obj` pointer is guaranteed to be valid.
84        unsafe { <T::Object as IntoGEMObject>::from_raw(self.op.gem.obj) }
85    }
86
87    /// The [`GpuVmBo`] that the new VA will be associated with.
88    pub fn vm_bo(&self) -> &GpuVmBo<T> {
89        self.vm_bo
90    }
91
92    /// Use the pre-allocated VA to carry out this map operation.
93    pub fn insert(self, va: GpuVaAlloc<T>, va_data: impl PinInit<T::VaData>) -> OpMapped<'op, T> {
94        let va = va.prepare(va_data);
95        // SAFETY: By the type invariants we may access the interval tree.
96        unsafe { bindings::drm_gpuva_map(self.vm_bo.gpuvm().as_raw(), va, self.op) };
97
98        let _gpuva_guard = self.vm_bo().lock_gpuva();
99        // SAFETY: The va is prepared for insertion, and we hold the GEM lock.
100        unsafe { bindings::drm_gpuva_link(va, self.vm_bo.as_raw()) };
101
102        OpMapped {
103            _invariant: self._invariant,
104        }
105    }
106}
107
108/// Represents a completed [`OpMap`] operation.
109pub struct OpMapped<'op, T> {
110    _invariant: PhantomData<*mut &'op mut T>,
111}
112
113/// Represents an `sm_step_unmap` operation that has not yet been completed.
114pub struct OpUnmap<'op, T: DriverGpuVm> {
115    op: &'op bindings::drm_gpuva_op_unmap,
116    // This ensures that 'op is invariant, so that `OpUnmap<'long, T>` does not
117    // coerce to `OpUnmap<'short, T>`. This ensures that the user can't return the
118    // wrong`OpUnmapped` value.
119    _invariant: PhantomData<*mut &'op mut T>,
120}
121
122impl<'op, T: DriverGpuVm> OpUnmap<'op, T> {
123    /// Indicates whether this [`GpuVa`] is physically contiguous with the
124    /// original mapping request.
125    ///
126    /// Optionally, if `keep` is set, drivers may keep the actual page table
127    /// mappings for this `drm_gpuva`, adding the missing page table entries
128    /// only and update the `drm_gpuvm` accordingly.
129    pub fn keep(&self) -> bool {
130        self.op.keep
131    }
132
133    /// The range being unmapped.
134    pub fn va(&self) -> &GpuVa<T> {
135        // SAFETY: This is a valid va. It's not the `kernel_alloc_node` because you can't unmap it,
136        // and it's not sparse by the `GpuVm<T>` type invariants.
137        unsafe { GpuVa::<T>::from_raw(self.op.va) }
138    }
139
140    /// Remove the VA.
141    pub fn remove(self) -> (OpUnmapped<'op, T>, GpuVaRemoved<T>) {
142        // SAFETY: The op references a valid drm_gpuva in the GPUVM.
143        unsafe { bindings::drm_gpuva_unmap(self.op) };
144        // SAFETY: The va is no longer in the interval tree so we may unlink it.
145        unsafe { bindings::drm_gpuva_unlink_defer(self.op.va) };
146
147        // SAFETY: We just removed this va from the `GpuVm<T>`.
148        let va = unsafe { GpuVaRemoved::from_raw(self.op.va) };
149
150        (
151            OpUnmapped {
152                _invariant: self._invariant,
153            },
154            va,
155        )
156    }
157}
158
159/// Represents a completed [`OpUnmap`] operation.
160pub struct OpUnmapped<'op, T> {
161    _invariant: PhantomData<*mut &'op mut T>,
162}
163
164/// Represents an `sm_step_remap` operation that has not yet been completed.
165pub struct OpRemap<'op, T: DriverGpuVm> {
166    op: &'op bindings::drm_gpuva_op_remap,
167    // This ensures that 'op is invariant, so that `OpRemap<'long, T>` does not
168    // coerce to `OpRemap<'short, T>`. This ensures that the user can't return the
169    // wrong`OpRemapped` value.
170    _invariant: PhantomData<*mut &'op mut T>,
171}
172
173impl<'op, T: DriverGpuVm> OpRemap<'op, T> {
174    /// The preceding part of a split mapping.
175    #[inline]
176    pub fn prev(&self) -> Option<&OpRemapMapData> {
177        // SAFETY: We checked for null, so the pointer must be valid.
178        NonNull::new(self.op.prev).map(|ptr| unsafe { OpRemapMapData::from_raw(ptr) })
179    }
180
181    /// The subsequent part of a split mapping.
182    #[inline]
183    pub fn next(&self) -> Option<&OpRemapMapData> {
184        // SAFETY: We checked for null, so the pointer must be valid.
185        NonNull::new(self.op.next).map(|ptr| unsafe { OpRemapMapData::from_raw(ptr) })
186    }
187
188    /// Indicates whether the `drm_gpuva` being removed is physically contiguous with the original
189    /// mapping request.
190    ///
191    /// Optionally, if `keep` is set, drivers may keep the actual page table mappings for this
192    /// `drm_gpuva`, adding the missing page table entries only and update the `drm_gpuvm`
193    /// accordingly.
194    #[inline]
195    pub fn keep(&self) -> bool {
196        // SAFETY: The unmap pointer is always valid.
197        unsafe { (*self.op.unmap).keep }
198    }
199
200    /// The range being unmapped.
201    #[inline]
202    pub fn va_to_unmap(&self) -> &GpuVa<T> {
203        // SAFETY: This is a valid va. It's not the `kernel_alloc_node` because you can't unmap it,
204        // and it's not sparse by the `GpuVm<T>` type invariants.
205        unsafe { GpuVa::<T>::from_raw((*self.op.unmap).va) }
206    }
207
208    /// The [`drm_gem_object`](DriverGpuVm::Object) whose VA is being remapped.
209    #[inline]
210    pub fn obj(&self) -> &T::Object {
211        self.va_to_unmap().obj()
212    }
213
214    /// The [`GpuVmBo`] that is being remapped.
215    #[inline]
216    pub fn vm_bo(&self) -> &GpuVmBo<T> {
217        self.va_to_unmap().vm_bo()
218    }
219
220    /// Update the GPUVM to perform the remapping.
221    pub fn remap(
222        self,
223        va_alloc: [GpuVaAlloc<T>; 2],
224        prev_data: impl PinInit<T::VaData>,
225        next_data: impl PinInit<T::VaData>,
226    ) -> (OpRemapped<'op, T>, OpRemapRet<T>) {
227        let [va1, va2] = va_alloc;
228
229        let mut unused_va = None;
230        let mut prev_ptr = ptr::null_mut();
231        let mut next_ptr = ptr::null_mut();
232        if self.prev().is_some() {
233            prev_ptr = va1.prepare(prev_data);
234        } else {
235            unused_va = Some(va1);
236        }
237        if self.next().is_some() {
238            next_ptr = va2.prepare(next_data);
239        } else {
240            unused_va = Some(va2);
241        }
242
243        // SAFETY: the pointers are non-null when required
244        unsafe { bindings::drm_gpuva_remap(prev_ptr, next_ptr, self.op) };
245
246        let gpuva_guard = self.vm_bo().lock_gpuva();
247        if !prev_ptr.is_null() {
248            // SAFETY: The prev_ptr is a valid drm_gpuva prepared for insertion. The vm_bo is still
249            // valid as the not-yet-unlinked gpuva holds a refcount on the vm_bo.
250            unsafe { bindings::drm_gpuva_link(prev_ptr, self.vm_bo().as_raw()) };
251        }
252        if !next_ptr.is_null() {
253            // SAFETY: The next_ptr is a valid drm_gpuva prepared for insertion. The vm_bo is still
254            // valid as the not-yet-unlinked gpuva holds a refcount on the vm_bo.
255            unsafe { bindings::drm_gpuva_link(next_ptr, self.vm_bo().as_raw()) };
256        }
257        drop(gpuva_guard);
258
259        // SAFETY: The va is no longer in the interval tree so we may unlink it.
260        unsafe { bindings::drm_gpuva_unlink_defer((*self.op.unmap).va) };
261
262        (
263            OpRemapped {
264                _invariant: self._invariant,
265            },
266            OpRemapRet {
267                // SAFETY: We just removed this va from the `GpuVm<T>`.
268                unmapped_va: unsafe { GpuVaRemoved::from_raw((*self.op.unmap).va) },
269                unused_va,
270            },
271        )
272    }
273}
274
275/// Part of an [`OpRemap`] that represents a new mapping.
276#[repr(transparent)]
277pub struct OpRemapMapData(bindings::drm_gpuva_op_map);
278
279impl OpRemapMapData {
280    /// # Safety
281    /// Must reference a valid `drm_gpuva_op_map` for duration of `'a`.
282    unsafe fn from_raw<'a>(ptr: NonNull<bindings::drm_gpuva_op_map>) -> &'a Self {
283        // SAFETY: ok per safety requirements
284        unsafe { ptr.cast().as_ref() }
285    }
286
287    /// The base address of the new mapping.
288    pub fn addr(&self) -> u64 {
289        self.0.va.addr
290    }
291
292    /// The length of the new mapping.
293    pub fn length(&self) -> u64 {
294        self.0.va.range
295    }
296
297    /// The offset within the [`drm_gem_object`](DriverGpuVm::Object).
298    pub fn gem_offset(&self) -> u64 {
299        self.0.gem.offset
300    }
301}
302
303/// Struct containing objects removed or not used by [`OpRemap::remap`].
304pub struct OpRemapRet<T: DriverGpuVm> {
305    /// The `drm_gpuva` that was removed.
306    pub unmapped_va: GpuVaRemoved<T>,
307    /// If the remap did not split the region into two pieces, then the unused `drm_gpuva` is
308    /// returned here.
309    pub unused_va: Option<GpuVaAlloc<T>>,
310}
311
312/// Represents a completed [`OpRemap`] operation.
313pub struct OpRemapped<'op, T> {
314    _invariant: PhantomData<*mut &'op mut T>,
315}
316
317impl<T: DriverGpuVm> UniqueRefGpuVm<T> {
318    /// Create a mapping, removing or remapping anything that overlaps.
319    ///
320    /// Internally calls the [`DriverGpuVm`] callbacks similar to [`Self::sm_unmap`], except that
321    /// the [`DriverGpuVm::sm_step_map`] is called once to create the requested mapping.
322    #[inline]
323    pub fn sm_map(&mut self, req: OpMapRequest<'_, '_, T>) -> Result {
324        if req.vm_bo.gpuvm() != &**self {
325            return Err(EINVAL);
326        }
327
328        let gpuvm = self.as_raw();
329        let raw_req = req.raw_request();
330        // INVARIANT: Checked above that `vm_bo.gpuvm() == self`.
331        let mut p = SmMapData {
332            sm_data: SmData {
333                gpuvm: self,
334                user_context: req.context,
335            },
336            vm_bo: req.vm_bo,
337        };
338        // SAFETY:
339        // * raw_request() creates a valid request.
340        // * The private data is valid to be interpreted as both SmData and SmMapData since the
341        //   first field of SmMapData is SmData.
342        to_result(unsafe {
343            bindings::drm_gpuvm_sm_map(gpuvm, (&raw mut p).cast(), &raw const raw_req)
344        })
345    }
346
347    /// Remove any mappings in the given region.
348    ///
349    /// Internally calls [`DriverGpuVm::sm_step_unmap`] for ranges entirely contained within the
350    /// given range, and [`DriverGpuVm::sm_step_remap`] for ranges that overlap with the range.
351    #[inline]
352    pub fn sm_unmap(&mut self, addr: u64, length: u64, context: &mut T::SmContext<'_>) -> Result {
353        let gpuvm = self.as_raw();
354        let mut p = SmData {
355            gpuvm: self,
356            user_context: context,
357        };
358        // SAFETY:
359        // * raw_request() creates a valid request.
360        // * The private data is a valid SmData.
361        to_result(unsafe { bindings::drm_gpuvm_sm_unmap(gpuvm, (&raw mut p).cast(), addr, length) })
362    }
363}
364
365impl<T: DriverGpuVm> GpuVm<T> {
366    /// # Safety
367    /// Must be called from `sm_map` with a pointer to `SmMapData`.
368    pub(super) unsafe extern "C" fn sm_step_map(
369        op: *mut bindings::drm_gpuva_op,
370        p: *mut c_void,
371    ) -> c_int {
372        // SAFETY: If we reach `sm_step_map` then we were called from `sm_map` which always passes
373        // an `SmMapData` as private data.
374        let p = unsafe { &mut *p.cast::<SmMapData<'_, '_, T>>() };
375        let op = OpMap {
376            // SAFETY: sm_step_map is called with a map operation.
377            op: unsafe { &(*op).__bindgen_anon_1.map },
378            vm_bo: p.vm_bo,
379            _invariant: PhantomData,
380        };
381        match p
382            .sm_data
383            .gpuvm
384            .data()
385            .sm_step_map(op, p.sm_data.user_context)
386        {
387            Ok(OpMapped { .. }) => 0,
388            Err(err) => err.to_errno(),
389        }
390    }
391
392    /// # Safety
393    /// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMapData` or `SmData`.
394    pub(super) unsafe extern "C" fn sm_step_unmap(
395        op: *mut bindings::drm_gpuva_op,
396        p: *mut c_void,
397    ) -> c_int {
398        // SAFETY: The caller provides a pointer that can be treated as `SmData`.
399        let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };
400        let op = OpUnmap {
401            // SAFETY: sm_step_unmap is called with an unmap operation.
402            op: unsafe { &(*op).__bindgen_anon_1.unmap },
403            _invariant: PhantomData,
404        };
405        match p.gpuvm.data().sm_step_unmap(op, p.user_context) {
406            Ok(OpUnmapped { .. }) => 0,
407            Err(err) => err.to_errno(),
408        }
409    }
410
411    /// # Safety
412    /// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMapData` or `SmData`.
413    pub(super) unsafe extern "C" fn sm_step_remap(
414        op: *mut bindings::drm_gpuva_op,
415        p: *mut c_void,
416    ) -> c_int {
417        // SAFETY: The caller provides a pointer that can be treated as `SmData`.
418        let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };
419        let op = OpRemap {
420            // SAFETY: sm_step_remap is called with a remap operation.
421            op: unsafe { &(*op).__bindgen_anon_1.remap },
422            _invariant: PhantomData,
423        };
424        match p.gpuvm.data().sm_step_remap(op, p.user_context) {
425            Ok(OpRemapped { .. }) => 0,
426            Err(err) => err.to_errno(),
427        }
428    }
429}