Skip to content

Commit a9b2a5c

Browse files
committed
rust: gpuvm: add GpuVmCore::sm_map()
Finally also add the operation for creating new mappings. Mapping operations need extra data in the context since they involve a vm_bo coming from the outside. Co-developed-by: Asahi Lina <lina+kernel@asahilina.net> Signed-off-by: Asahi Lina <lina+kernel@asahilina.net> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Signed-off-by: Alice Ryhl <aliceryhl@google.com>
1 parent d343424 commit a9b2a5c

2 files changed

Lines changed: 170 additions & 6 deletions

File tree

rust/kernel/drm/gpuvm/mod.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ impl<T: DriverGpuVm> GpuVm<T> {
108108
vm_bo_alloc: GpuVmBo::<T>::ALLOC_FN,
109109
vm_bo_free: GpuVmBo::<T>::FREE_FN,
110110
vm_bo_validate: None,
111-
sm_step_map: None,
111+
sm_step_map: Some(Self::sm_step_map),
112112
sm_step_unmap: Some(Self::sm_step_unmap),
113113
sm_step_remap: Some(Self::sm_step_remap),
114114
}
@@ -266,6 +266,13 @@ pub trait DriverGpuVm: Sized + Send {
266266
/// The private data passed to callbacks.
267267
type SmContext<'ctx>;
268268

269+
/// Indicates that a new mapping should be created.
270+
fn sm_step_map<'op, 'ctx>(
271+
&mut self,
272+
op: OpMap<'op, Self>,
273+
context: &mut Self::SmContext<'ctx>,
274+
) -> Result<OpMapped<'op, Self>, Error>;
275+
269276
/// Indicates that an existing mapping should be removed.
270277
fn sm_step_unmap<'op, 'ctx>(
271278
&mut self,

rust/kernel/drm/gpuvm/sm_ops.rs

Lines changed: 162 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,108 @@ struct SmData<'a, 'ctx, T: DriverGpuVm> {
88
user_context: &'a mut T::SmContext<'ctx>,
99
}
1010

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)]
17+
struct 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`].
23+
pub struct OpMapRequest<'a, 'ctx, T: DriverGpuVm> {
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+
36+
impl<'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.
54+
pub 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+
65+
impl<'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.
109+
pub struct OpMapped<'op, T> {
110+
_invariant: PhantomData<*mut &'op mut T>,
111+
}
112+
11113
/// Represents an `sm_step_unmap` operation that has not yet been completed.
12114
pub struct OpUnmap<'op, T: DriverGpuVm> {
13115
op: &'op bindings::drm_gpuva_op_unmap,
@@ -213,6 +315,35 @@ pub struct OpRemapped<'op, T> {
213315
}
214316

215317
impl<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+
216347
/// Remove any mappings in the given region.
217348
///
218349
/// Internally calls [`DriverGpuVm::sm_step_unmap`] for ranges entirely contained within the
@@ -226,19 +357,45 @@ impl<T: DriverGpuVm> UniqueRefGpuVm<T> {
226357
};
227358
// SAFETY:
228359
// * raw_request() creates a valid request.
229-
// * The private data is valid to be interpreted as SmData.
360+
// * The private data is a valid SmData.
230361
to_result(unsafe { bindings::drm_gpuvm_sm_unmap(gpuvm, (&raw mut p).cast(), addr, length) })
231362
}
232363
}
233364

234365
impl<T: DriverGpuVm> GpuVm<T> {
235366
/// # Safety
236-
/// Must be called from `sm_unmap` with a pointer to `SmData`.
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`.
237394
pub(super) unsafe extern "C" fn sm_step_unmap(
238395
op: *mut bindings::drm_gpuva_op,
239396
p: *mut c_void,
240397
) -> c_int {
241-
// SAFETY: The caller provides a pointer to `SmData`.
398+
// SAFETY: The caller provides a pointer that can be treated as `SmData`.
242399
let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };
243400
let op = OpUnmap {
244401
// SAFETY: sm_step_unmap is called with an unmap operation.
@@ -252,12 +409,12 @@ impl<T: DriverGpuVm> GpuVm<T> {
252409
}
253410

254411
/// # Safety
255-
/// Must be called from `sm_unmap` with a pointer to `SmData`.
412+
/// Must be called from `sm_map` or `sm_unmap` with a pointer to `SmMapData` or `SmData`.
256413
pub(super) unsafe extern "C" fn sm_step_remap(
257414
op: *mut bindings::drm_gpuva_op,
258415
p: *mut c_void,
259416
) -> c_int {
260-
// SAFETY: The caller provides a pointer to `SmData`.
417+
// SAFETY: The caller provides a pointer that can be treated as `SmData`.
261418
let p = unsafe { &mut *p.cast::<SmData<'_, '_, T>>() };
262419
let op = OpRemap {
263420
// SAFETY: sm_step_remap is called with a remap operation.

0 commit comments

Comments
 (0)