1 // SPDX-License-Identifier: GPL-2.0 OR MIT 2 3 use super::*; 4 5 /// The actual data that gets threaded through the callbacks. 6 struct SmData<'a, 'ctx, T: DriverGpuVm> { 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)] 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 113 /// Represents an `sm_step_unmap` operation that has not yet been completed. 114 pub 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 122 impl<'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. 160 pub 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. 165 pub 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 173 impl<'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)] 277 pub struct OpRemapMapData(bindings::drm_gpuva_op_map); 278 279 impl 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`]. 304 pub 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. 313 pub struct OpRemapped<'op, T> { 314 _invariant: PhantomData<*mut &'op mut T>, 315 } 316 317 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 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 365 impl<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 } 430