One of the more obvious use cases for gem shmem objects is the ability to create mappings into their contents, specifically iosys mappings. Now that we've added iosys_map rust bindings to the kernel, let's hook these up in gem shmem.
Similar to how we handle SGTables, we make sure there's two different types of mappings: owned mappings (kernel::drm::gem::shmem::VMap) and borrowed mappings (kernel::drm::gem::shmem::VMapRef). One last note: we change the #[expect(unused)] for RawIoSysMap::from_raw() to an #[allow(unused)]. Normally we would simply remove the lint assertion, however - since shmem is conditionally built, we need allow to avoid hitting warnings in certain kernel configurations. Signed-off-by: Lyude Paul <[email protected]> --- V7: * Switch over to the new iosys map bindings that use the Io trait rust/kernel/drm/gem/shmem.rs | 170 ++++++++++++++++++++++++++++++++++- rust/kernel/iosys_map.rs | 3 +- 2 files changed, 170 insertions(+), 3 deletions(-) diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index e511a9b6710e0..604fb10325d1e 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -21,6 +21,7 @@ from_err_ptr, to_result, // }, + iosys_map::*, prelude::*, scatterlist, types::{ @@ -29,13 +30,18 @@ }, // }; use core::{ + mem::{ + self, + MaybeUninit, // + }, ops::{ Deref, DerefMut, // }, - ptr::NonNull, + ptr::NonNull, // }; use gem::{ + BaseObject, BaseObjectPrivate, DriverObject, IntoGEMObject, // @@ -216,6 +222,72 @@ pub fn owned_sg_table(&self) -> Result<SGTable<T>> { _owner: self.into(), }) } + + /// Attempt to create a [`RawIoSysMap`] from the gem object. + fn raw_vmap<const SIZE: usize>(&self) -> Result<RawIoSysMap<SIZE>> { + let mut map: MaybeUninit<bindings::iosys_map> = MaybeUninit::uninit(); + + // SAFETY: drm_gem_shmem_vmap can be called with the DMA reservation lock held + to_result(unsafe { + // TODO: see top of file + bindings::dma_resv_lock(self.raw_dma_resv(), core::ptr::null_mut()); + let ret = bindings::drm_gem_shmem_vmap_locked(self.as_shmem(), map.as_mut_ptr()); + bindings::dma_resv_unlock(self.raw_dma_resv()); + ret + })?; + + // SAFETY: if drm_gem_shmem_vmap did not fail, map is initialized now + Ok(unsafe { RawIoSysMap::<SIZE>::from_raw(map.assume_init()) }) + } + + /// Unmap a [`RawIoSysMap`] from the gem object. + /// + /// # Safety + /// + /// - The caller promises that `map` came from a prior call to [`Self::raw_vmap`] on this gem + /// object. + /// - The caller promises that the memory pointed to by `map` will no longer be accesed through + /// this instance. + unsafe fn raw_vunmap<const SIZE: usize>(&self, map: &mut RawIoSysMap<SIZE>) { + let resv = self.raw_dma_resv(); + + // SAFETY: + // - This function is safe to call with the DMA reservation lock held + // - Our `ARef` is proof that the underlying gem object here is initialized and thus safe to + // dereference. + unsafe { + // TODO: see top of file + bindings::dma_resv_lock(resv, core::ptr::null_mut()); + bindings::drm_gem_shmem_vunmap_locked(self.as_shmem(), map.as_raw_mut()); + bindings::dma_resv_unlock(resv); + } + } + + /// Creates and returns a virtual kernel memory mapping for this object. + pub fn vmap<const SIZE: usize>(&self) -> Result<VMapRef<'_, T, SIZE>> { + let map = self.raw_vmap()?; + + Ok(VMapRef { + // SAFETY: + // - The size of the vmap is the same as the size of the gem + // - The vmap will remain alive until this object is dropped. + map: unsafe { IoSysMapRef::<SIZE>::new(map, self.size()) }?, + owner: self, + }) + } + + /// Creates and returns an owned reference to a virtual kernel memory mapping for this object. + pub fn owned_vmap<const SIZE: usize>(&self) -> Result<VMap<T, SIZE>> { + // INVARIANT: We verify here that the mapping is at least of SIZE bytes. + if self.size() < SIZE { + return Err(EINVAL); + } + + Ok(VMap { + map: self.raw_vmap()?, + owner: self.into(), + }) + } } impl<T: DriverObject> Deref for Object<T> { @@ -267,6 +339,102 @@ impl<T: DriverObject> driver::AllocImpl for Object<T> { }; } +/// A borrowed reference to a virtual mapping for a shmem-based GEM object in kernel address space. +pub struct VMapRef<'a, D: DriverObject, const SIZE: usize = 0> { + map: IoSysMapRef<'a, SIZE>, + owner: &'a Object<D>, +} + +impl<'a, D: DriverObject, const SIZE: usize> Clone for VMapRef<'a, D, SIZE> { + fn clone(&self) -> Self { + // SAFETY: We have a successful vmap already, so this can't fail + unsafe { self.owner.vmap().unwrap_unchecked() } + } +} + +impl<'a, D: DriverObject, const SIZE: usize> Deref for VMapRef<'a, D, SIZE> { + type Target = IoSysMapRef<'a, SIZE>; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +impl<'a, D: DriverObject, const SIZE: usize> DerefMut for VMapRef<'a, D, SIZE> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.map + } +} + +impl<'a, D: DriverObject, const SIZE: usize> Drop for VMapRef<'a, D, SIZE> { + fn drop(&mut self) { + // SAFETY: Our existence is proof that this map was previously created using self.owner. + unsafe { self.owner.raw_vunmap(&mut self.map.raw_map) }; + } +} + +/// An owned reference to a virtual mapping for a shmem-based GEM object in kernel address space. +/// +/// # Invariants +/// +/// - The size of `owner` is >= SIZE. +/// - The memory pointed to by `map` is at least as large as `T`. +/// - The memory pointed to by `map` remains valid at least until this object is dropped. +pub struct VMap<D: DriverObject, const SIZE: usize = 0> { + map: RawIoSysMap<SIZE>, + owner: ARef<Object<D>>, +} + +impl<D: DriverObject, const SIZE: usize> Clone for VMap<D, SIZE> { + fn clone(&self) -> Self { + // SAFETY: We have a successful vmap already, so this can't fail + unsafe { self.owner.owned_vmap().unwrap_unchecked() } + } +} + +impl<'a, D: DriverObject, const SIZE: usize> From<VMapRef<'a, D, SIZE>> for VMap<D, SIZE> { + fn from(value: VMapRef<'a, D, SIZE>) -> Self { + let this = Self { + map: value.map.raw_map.clone(), + owner: value.owner.into(), + }; + + mem::forget(value); + this + } +} + +impl<D: DriverObject, const SIZE: usize> VMap<D, SIZE> { + /// Return a reference to the iosys map for this `VMap`. + #[inline] + pub fn get(&self) -> IoSysMapRef<'_, SIZE> { + // SAFETY: + // - The size of the iosys_map is equivalent to the size of the gem object. + // - `size` is >= SIZE according to our type invariants, ensuring that we can never pass an + // invalid `size` to `IoSysMapRef::new(). + unsafe { + IoSysMapRef::new(self.map.clone(), self.owner.size()).unwrap_unchecked() + } + } + + /// Borrows a reference to the object that owns this virtual mapping. + pub fn owner(&self) -> &Object<D> { + &self.owner + } +} + +impl<D: DriverObject, const SIZE: usize> Drop for VMap<D, SIZE> { + fn drop(&mut self) { + // SAFETY: Our existence is proof that this map was previously created using self.owner + unsafe { self.owner.raw_vunmap(&mut self.map) }; + } +} + +/// SAFETY: `iosys_map` objects are safe to send across threads. +unsafe impl<D: DriverObject, const SIZE: usize> Send for VMap<D, SIZE> {} +/// SAFETY: `iosys_map` objects are safe to send across threads. +unsafe impl<D: DriverObject, const SIZE: usize> Sync for VMap<D, SIZE> {} + /// An owned reference to a scatter-gather table of DMA address spans for a GEM shmem object. /// /// This object holds an owned reference to the underlying GEM shmem object, ensuring that the diff --git a/rust/kernel/iosys_map.rs b/rust/kernel/iosys_map.rs index 2070f0fb42cb8..b649d2de83093 100644 --- a/rust/kernel/iosys_map.rs +++ b/rust/kernel/iosys_map.rs @@ -33,7 +33,7 @@ impl<const SIZE: usize> RawIoSysMap<SIZE> { /// Convert from a raw `bindings::iosys_map`. - #[expect(unused)] + #[allow(unused)] #[inline] pub(crate) fn from_raw(val: bindings::iosys_map) -> Self { Self(val) @@ -139,7 +139,6 @@ impl<'a, const SIZE: usize> IoSysMapRef<'a, SIZE> { /// /// - The caller guarantees that the mapping is of at least `size` bytes. /// - The caller guarantees that the mapping remains valid for the lifetime of `'a`. - #[expect(unused)] pub(crate) unsafe fn new(map: RawIoSysMap<SIZE>, size: usize) -> Result<Self> { if size < SIZE { return Err(EINVAL); -- 2.53.0
