@@ -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.
12114pub struct OpUnmap < ' op , T : DriverGpuVm > {
13115 op : & ' op bindings:: drm_gpuva_op_unmap ,
@@ -213,6 +315,35 @@ pub struct OpRemapped<'op, T> {
213315}
214316
215317impl < 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
234365impl < 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