[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20260206223431.693765-8-lyude@redhat.com>
Date: Fri, 6 Feb 2026 17:34:31 -0500
From: Lyude Paul <lyude@...hat.com>
To: linux-kernel@...r.kernel.org,
dri-devel@...ts.freedesktop.org,
rust-for-linux@...r.kernel.org,
Danilo Krummrich <dakr@...nel.org>
Cc: nouveau@...ts.freedesktop.org,
Daniel Almeida <daniel.almeida@...labora.com>,
Gary Guo <gary@...yguo.net>,
Benno Lossin <lossin@...nel.org>,
Alexandre Courbot <acourbot@...dia.com>,
Janne Grunau <j@...nau.net>
Subject: [PATCH v7 7/7] rust: drm/gem: Add vmap functions to shmem bindings
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 <lyude@...hat.com>
---
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
Powered by blists - more mailing lists