* [PATCH v20 0/4] Rust bindings for gem shmem
@ 2026-06-10 16:21 ` Lyude Paul
0 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Thomas Zimmermann, Maxime Ripard, David Airlie, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Lyude Paul, Greg Kroah-Hartman
Most of this patch series has already been pushed upstream, this is just
the second half of the patch series that has not been pushed yet + some
additional changes which were required to implement changes requested by
the mailing list. This patch series is originally from Asahi, previously
posted by Daniel Almeida.
The previous version of the patch series can be found here:
https://patchwork.freedesktop.org/series/164580/
Branch with patches applied available here:
https://gitlab.freedesktop.org/lyudess/linux/-/commits/rust/gem-shmem
This patch series applies on top of drm-rust-next
Patch-series wide changes since V15:
* Fix some major rebasing errors I somehow didn't notice :(
* Drop the dependency on LazyInit, use the trick that Alice suggested
instead.
* Fix dependency ordering so that Tyr can get the vmap stuff first
without the other bits.
Patch-series wide changes since V16:
* Fix ordering one more time (SetOnce::reset() doesn't need to come
before adding vmap functions)
* Rebase against the latest DeviceContext changes from me that got
pushed.
Patch-series wide changes since V20:
* Lots of Sashiko fixes, excluding the comments that I couldn't prove
weren't just bogus.
Lyude Paul (4):
rust: drm: gem: shmem: Add DmaResvGuard helper
rust: drm: gem: shmem: Add vmap functions
rust: faux: Allow retrieving a bound Device
rust: drm: gem: Introduce shmem::Object::sg_table()
rust/kernel/drm/gem/shmem.rs | 546 ++++++++++++++++++++++++++++++++++-
rust/kernel/faux.rs | 16 +-
2 files changed, 546 insertions(+), 16 deletions(-)
base-commit: 848bf57e98e1678ce7a49eb4e0bf0502da95dc07
--
2.54.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v20 0/4] Rust bindings for gem shmem
@ 2026-06-10 16:21 ` Lyude Paul
0 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Maxime Ripard, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
Most of this patch series has already been pushed upstream, this is just
the second half of the patch series that has not been pushed yet + some
additional changes which were required to implement changes requested by
the mailing list. This patch series is originally from Asahi, previously
posted by Daniel Almeida.
The previous version of the patch series can be found here:
https://patchwork.freedesktop.org/series/164580/
Branch with patches applied available here:
https://gitlab.freedesktop.org/lyudess/linux/-/commits/rust/gem-shmem
This patch series applies on top of drm-rust-next
Patch-series wide changes since V15:
* Fix some major rebasing errors I somehow didn't notice :(
* Drop the dependency on LazyInit, use the trick that Alice suggested
instead.
* Fix dependency ordering so that Tyr can get the vmap stuff first
without the other bits.
Patch-series wide changes since V16:
* Fix ordering one more time (SetOnce::reset() doesn't need to come
before adding vmap functions)
* Rebase against the latest DeviceContext changes from me that got
pushed.
Patch-series wide changes since V20:
* Lots of Sashiko fixes, excluding the comments that I couldn't prove
weren't just bogus.
Lyude Paul (4):
rust: drm: gem: shmem: Add DmaResvGuard helper
rust: drm: gem: shmem: Add vmap functions
rust: faux: Allow retrieving a bound Device
rust: drm: gem: Introduce shmem::Object::sg_table()
rust/kernel/drm/gem/shmem.rs | 546 ++++++++++++++++++++++++++++++++++-
rust/kernel/faux.rs | 16 +-
2 files changed, 546 insertions(+), 16 deletions(-)
base-commit: 848bf57e98e1678ce7a49eb4e0bf0502da95dc07
--
2.54.0
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v20 1/4] rust: drm: gem: shmem: Add DmaResvGuard helper
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-10 16:21 ` Lyude Paul
-1 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Thomas Zimmermann, Maxime Ripard, David Airlie, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Lyude Paul, Greg Kroah-Hartman
Just a temporary holdover to make locking/unlocking the dma_resv lock much
easier.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Co-authored-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
V17:
* Fix format of commit message title
V19:
* Add NotThreadSafe to DmaResvGuard
V20:
* s/inline(always)/inline/
rust/kernel/drm/gem/shmem.rs | 39 ++++++++++++++++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
index 084b798ce795b..090c5d869fdb7 100644
--- a/rust/kernel/drm/gem/shmem.rs
+++ b/rust/kernel/drm/gem/shmem.rs
@@ -22,7 +22,10 @@
error::to_result,
prelude::*,
sync::aref::ARef,
- types::Opaque, //
+ types::{
+ NotThreadSafe,
+ Opaque, //
+ },
};
use core::{
marker::PhantomData,
@@ -30,7 +33,10 @@
Deref,
DerefMut, //
},
- ptr::NonNull, //
+ ptr::{
+ self,
+ NonNull, //
+ },
};
use gem::{
BaseObjectPrivate,
@@ -244,3 +250,32 @@ impl<T: DriverObject, C: DeviceContext> driver::AllocImpl for Object<T, C> {
dumb_map_offset: None,
};
}
+
+/// Private helper-type for holding the `dma_resv` object for a GEM shmem object.
+///
+/// When this is dropped, the `dma_resv` lock is dropped as well.
+///
+// TODO: This should be replace with a WwMutex equivalent once we have such bindings in the kernel.
+struct DmaResvGuard<'a, T: DriverObject, C: DeviceContext = Registered>(
+ &'a Object<T, C>,
+ NotThreadSafe,
+);
+
+impl<'a, T: DriverObject, C: DeviceContext> DmaResvGuard<'a, T, C> {
+ #[inline]
+ #[expect(unused)]
+ fn new(obj: &'a Object<T, C>) -> Self {
+ // SAFETY: This lock is initialized throughout the lifetime of `object`.
+ unsafe { bindings::dma_resv_lock(obj.raw_dma_resv(), ptr::null_mut()) };
+
+ Self(obj, NotThreadSafe)
+ }
+}
+
+impl<'a, T: DriverObject, C: DeviceContext> Drop for DmaResvGuard<'a, T, C> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: We are releasing the lock grabbed during the creation of this object.
+ unsafe { bindings::dma_resv_unlock(self.0.raw_dma_resv()) };
+ }
+}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v20 1/4] rust: drm: gem: shmem: Add DmaResvGuard helper
@ 2026-06-10 16:21 ` Lyude Paul
0 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Maxime Ripard, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
Just a temporary holdover to make locking/unlocking the dma_resv lock much
easier.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Co-authored-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
V17:
* Fix format of commit message title
V19:
* Add NotThreadSafe to DmaResvGuard
V20:
* s/inline(always)/inline/
rust/kernel/drm/gem/shmem.rs | 39 ++++++++++++++++++++++++++++++++++--
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
index 084b798ce795b..090c5d869fdb7 100644
--- a/rust/kernel/drm/gem/shmem.rs
+++ b/rust/kernel/drm/gem/shmem.rs
@@ -22,7 +22,10 @@
error::to_result,
prelude::*,
sync::aref::ARef,
- types::Opaque, //
+ types::{
+ NotThreadSafe,
+ Opaque, //
+ },
};
use core::{
marker::PhantomData,
@@ -30,7 +33,10 @@
Deref,
DerefMut, //
},
- ptr::NonNull, //
+ ptr::{
+ self,
+ NonNull, //
+ },
};
use gem::{
BaseObjectPrivate,
@@ -244,3 +250,32 @@ impl<T: DriverObject, C: DeviceContext> driver::AllocImpl for Object<T, C> {
dumb_map_offset: None,
};
}
+
+/// Private helper-type for holding the `dma_resv` object for a GEM shmem object.
+///
+/// When this is dropped, the `dma_resv` lock is dropped as well.
+///
+// TODO: This should be replace with a WwMutex equivalent once we have such bindings in the kernel.
+struct DmaResvGuard<'a, T: DriverObject, C: DeviceContext = Registered>(
+ &'a Object<T, C>,
+ NotThreadSafe,
+);
+
+impl<'a, T: DriverObject, C: DeviceContext> DmaResvGuard<'a, T, C> {
+ #[inline]
+ #[expect(unused)]
+ fn new(obj: &'a Object<T, C>) -> Self {
+ // SAFETY: This lock is initialized throughout the lifetime of `object`.
+ unsafe { bindings::dma_resv_lock(obj.raw_dma_resv(), ptr::null_mut()) };
+
+ Self(obj, NotThreadSafe)
+ }
+}
+
+impl<'a, T: DriverObject, C: DeviceContext> Drop for DmaResvGuard<'a, T, C> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: We are releasing the lock grabbed during the creation of this object.
+ unsafe { bindings::dma_resv_unlock(self.0.raw_dma_resv()) };
+ }
+}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v20 2/4] rust: drm: gem: shmem: Add vmap functions
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-10 16:21 ` Lyude Paul
-1 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Thomas Zimmermann, Maxime Ripard, David Airlie, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Lyude Paul, Greg Kroah-Hartman
One of the more obvious use cases for gem shmem objects is the ability to
create mappings into their contents. So, let's hook this up in our rust
bindings.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
---
V7:
* Switch over to the new iosys map bindings that use the Io trait
V8:
* Get rid of iosys_map bindings for now, only support non-iomem types
* s/as_shmem()/as_raw_shmem()
V9:
* Get rid of some outdated comments I missed
* Add missing SIZE check to raw_vmap()
* Add a proper unit test that ensures that we actually validate SIZE at
compile-time.
Turns out it takes only 34 lines to make a boilerplate DRM driver for a
kunit test :)
* Add unit tests
* Add some missing #[inline]s
V10:
* Correct issue with iomem error path
We previously called raw_vunmap() if we got an iomem allocation, but
raw_vunmap() was written such that it assumed all allocations were sysmem
allocations. Fix this by just making raw_vunmap() accept a iosys_map.
V11:
* Use Alexandre's clever solution to remove the macros we were using for
maintaining two different VMap types.
* Change the order of items in Object<T> to ensure that sgt_res is always
dropped before obj.
* Fix typo in Object.raw_vmap()
* s/raw_vmap()/make_vmap()/
Deduplicate code a bit more as well by using more generics here
V15:
* Add these patches back
* We only have one VMap type now!
* Use ObjectConfig::default() in unit tests since we unbroke it.
V16:
* Fix huge rebase error I made and did not notice that squashed 1.5 patches
together that were definitely not supposed to be squashed
* Update old commit message
V17:
* Rebase
* Fix format of commit message title
V19:
* Drop outdated safety comment
* Move impl_vmap_io_capable! definition to right before it gets used
* Add missing `` in rustdoc for VMap type
* Add a bunch of missing `` in make_vmap()
* Remove one outdated safety comment about reading vaddr_iomem
* Add some missing periods in safety comments in make_vmap().
* Use read_volatile/write_volatile() instead of read()/write() to prevent
compiler reordering.
* Remove impl argument from impl_vmap_io_capable!()
* Check .owner() and .maxsize() in compile_time_vmap_sizes()
* Use more varied pattern in vmap_io()
V20:
* Add missing Send/Sync implementations for VMap
* Use #[inline] not #[inline(always)]
* Add missing invariant comment to VMap instantiation
* Make sure that kunit test doesn't fail on big endian
rust/kernel/drm/gem/shmem.rs | 337 ++++++++++++++++++++++++++++++++++-
1 file changed, 336 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
index 090c5d869fdb7..68a1ce3330b11 100644
--- a/rust/kernel/drm/gem/shmem.rs
+++ b/rust/kernel/drm/gem/shmem.rs
@@ -20,6 +20,11 @@
Registered, //
},
error::to_result,
+ io::{
+ Io,
+ IoCapable,
+ IoKnownSize, //
+ },
prelude::*,
sync::aref::ARef,
types::{
@@ -28,7 +33,9 @@
},
};
use core::{
+ ffi::c_void,
marker::PhantomData,
+ mem::MaybeUninit, //
ops::{
Deref,
DerefMut, //
@@ -39,6 +46,7 @@
},
};
use gem::{
+ BaseObject,
BaseObjectPrivate,
DriverObject,
IntoGEMObject, //
@@ -200,6 +208,79 @@ extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
// SAFETY: We're recovering the Kbox<> we created in gem_create_object()
let _ = unsafe { KBox::from_raw(this) };
}
+
+ /// Attempt to create a vmap from the gem object, and confirm the size of said vmap.
+ fn make_vmap<'a, R, const SIZE: usize>(&'a self) -> Result<VMap<T, R, C, SIZE>>
+ where
+ R: Deref<Target = Self> + From<&'a Self>,
+ {
+ // INVARIANT: We check here that the gem object is at least as large as `SIZE`.
+ if self.size() < SIZE {
+ return Err(ENOSPC);
+ }
+
+ let mut map: MaybeUninit<bindings::iosys_map> = MaybeUninit::uninit();
+ let guard = DmaResvGuard::new(self);
+
+ // SAFETY: `drm_gem_shmem_vmap()` can be called with the DMA reservation lock held.
+ to_result(unsafe {
+ bindings::drm_gem_shmem_vmap_locked(self.as_raw_shmem(), map.as_mut_ptr())
+ })?;
+
+ // Drop the guard explicitly here, since we may need to call `raw_vunmap()` (which
+ // re-acquires the lock).
+ drop(guard);
+
+ // SAFETY: The call to `drm_gem_shmem_vmap_locked()` succeeded above, so we are guaranteed
+ // that map is properly initialized.
+ let map = unsafe { map.assume_init() };
+
+ // XXX: We don't currently support iomem allocations
+ if map.is_iomem {
+ // SAFETY: The vmap operation above succeeded, guaranteeing that `map` points to a valid
+ // memory mapping.
+ unsafe { self.raw_vunmap(map) };
+
+ Err(ENOTSUPP)
+ } else {
+ Ok(VMap {
+ // INVARIANT: `addr` remains valid for as long as `owner` does, which extends to the
+ // lifetime of `VMap` itself.
+ // SAFETY: We checked that this is not an iomem allocation, making it safe to read
+ // vaddr.
+ addr: unsafe { map.__bindgen_anon_1.vaddr },
+ owner: self.into(),
+ })
+ }
+ }
+
+ /// Unmap a vmap from the gem object.
+ ///
+ /// # Safety
+ ///
+ /// - The caller promises that `map` is a valid 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(&self, mut map: bindings::iosys_map) {
+ let _guard = DmaResvGuard::new(self);
+
+ // SAFETY:
+ // - This function is safe to call with the DMA reservation lock held.
+ // - The caller promises that `map` is a valid vmap on this gem object.
+ unsafe { bindings::drm_gem_shmem_vunmap_locked(self.as_raw_shmem(), &mut map) };
+ }
+
+ /// Creates and returns a virtual kernel memory mapping for this object.
+ #[inline]
+ pub fn vmap<const SIZE: usize>(&self) -> Result<VMapRef<'_, T, C, SIZE>> {
+ self.make_vmap()
+ }
+
+ /// Creates and returns an owned reference to a virtual kernel memory mapping for this object.
+ #[inline]
+ pub fn owned_vmap<const SIZE: usize>(&self) -> Result<VMapOwned<T, C, SIZE>> {
+ self.make_vmap()
+ }
}
impl<T: DriverObject, C: DeviceContext> Deref for Object<T, C> {
@@ -263,7 +344,6 @@ struct DmaResvGuard<'a, T: DriverObject, C: DeviceContext = Registered>(
impl<'a, T: DriverObject, C: DeviceContext> DmaResvGuard<'a, T, C> {
#[inline]
- #[expect(unused)]
fn new(obj: &'a Object<T, C>) -> Self {
// SAFETY: This lock is initialized throughout the lifetime of `object`.
unsafe { bindings::dma_resv_lock(obj.raw_dma_resv(), ptr::null_mut()) };
@@ -279,3 +359,258 @@ fn drop(&mut self) {
unsafe { bindings::dma_resv_unlock(self.0.raw_dma_resv()) };
}
}
+
+/// A reference to a virtual mapping for an shmem-based GEM object in kernel address space.
+///
+/// # Invariants
+///
+/// - The size of `owner` is >= SIZE.
+/// - The memory pointed to by `addr` remains valid at least until this object is dropped.
+pub struct VMap<D, R, C = Registered, const SIZE: usize = 0>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ addr: *mut c_void,
+ owner: R,
+}
+
+/// An alias type for a reference to a shmem-based GEM object's VMap.
+pub type VMapRef<'a, D, C, const SIZE: usize = 0> = VMap<D, &'a Object<D, C>, C, SIZE>;
+
+/// An alias type for an owned reference to a shmem-based GEM object's VMap.
+pub type VMapOwned<D, C, const SIZE: usize = 0> = VMap<D, ARef<Object<D, C>>, C, SIZE>;
+
+impl<D, R, C, const SIZE: usize> VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ /// Borrows a reference to the object that owns this virtual mapping.
+ #[inline]
+ pub fn owner(&self) -> &Object<D, C> {
+ &self.owner
+ }
+}
+
+impl<D, R, C, const SIZE: usize> Drop for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY:
+ // - Our existence is proof that this map was previously created using self.owner.
+ // - Since we are in Drop, we are guaranteed that no one will access the memory
+ // through this mapping after calling this.
+ unsafe {
+ self.owner.raw_vunmap(bindings::iosys_map {
+ is_iomem: false,
+ __bindgen_anon_1: bindings::iosys_map__bindgen_ty_1 { vaddr: self.addr },
+ })
+ };
+ }
+}
+
+// SAFETY: VMaps are safe to send across threads.
+unsafe impl<D, R, C, const SIZE: usize> Send for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+}
+
+// SAFETY: VMaps are safe to access from multiple threads.
+unsafe impl<D, R, C, const SIZE: usize> Sync for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+}
+
+impl<D, R, C, const SIZE: usize> Io for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ #[inline]
+ fn addr(&self) -> usize {
+ self.addr as usize
+ }
+
+ #[inline]
+ fn maxsize(&self) -> usize {
+ self.owner.size()
+ }
+}
+
+impl<D, R, C, const SIZE: usize> IoKnownSize for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ const MIN_SIZE: usize = SIZE;
+}
+
+macro_rules! impl_vmap_io_capable {
+ ($ty:ty) => {
+ impl<D, R, C, const SIZE: usize> IoCapable<$ty> for VMap<D, R, C, SIZE>
+ where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+ {
+ #[inline]
+ unsafe fn io_read(&self, address: usize) -> $ty {
+ let ptr = address as *mut $ty;
+
+ // SAFETY: The safety contract of `io_read` guarantees that address is a valid
+ // address within the bounds of `Self` of at least the size of $ty, and is properly
+ // aligned.
+ unsafe { ptr::read_volatile(ptr) }
+ }
+
+ #[inline]
+ unsafe fn io_write(&self, value: $ty, address: usize) {
+ let ptr = address as *mut $ty;
+
+ // SAFETY: The safety contract of `io_write` guarantees that address is a valid
+ // address within the bounds of `Self` of at least the size of $ty, and is properly
+ // aligned.
+ unsafe { ptr::write_volatile(ptr, value) }
+ }
+ }
+ };
+}
+
+impl_vmap_io_capable!(u8);
+impl_vmap_io_capable!(u16);
+impl_vmap_io_capable!(u32);
+#[cfg(CONFIG_64BIT)]
+impl_vmap_io_capable!(u64);
+
+#[kunit_tests(rust_drm_gem_shmem)]
+mod tests {
+ use super::*;
+ use crate::{
+ drm::{
+ self,
+ UnregisteredDevice, //
+ },
+ faux,
+ page::PAGE_SIZE, //
+ };
+
+ // The bare minimum needed to create a fake drm driver for kunit
+
+ #[pin_data]
+ struct KunitData {}
+ struct KunitDriver;
+ struct KunitFile;
+ #[pin_data]
+ struct KunitObject {}
+
+ const INFO: drm::DriverInfo = drm::DriverInfo {
+ major: 0,
+ minor: 0,
+ patchlevel: 0,
+ name: c"kunit",
+ desc: c"Kunit",
+ };
+
+ impl drm::file::DriverFile for KunitFile {
+ type Driver = KunitDriver;
+
+ fn open(_dev: &drm::Device<KunitDriver>) -> Result<Pin<KBox<Self>>> {
+ Ok(KBox::new(Self, GFP_KERNEL)?.into())
+ }
+ }
+
+ impl gem::DriverObject for KunitObject {
+ type Driver = KunitDriver;
+ type Args = ();
+
+ fn new<C: DeviceContext>(
+ _dev: &drm::Device<KunitDriver, C>,
+ _size: usize,
+ _args: Self::Args,
+ ) -> impl PinInit<Self, Error> {
+ try_pin_init!(KunitObject {})
+ }
+ }
+
+ #[vtable]
+ impl drm::Driver for KunitDriver {
+ type Data = KunitData;
+ type File = KunitFile;
+ type Object<Ctx: DeviceContext> = Object<KunitObject, Ctx>;
+
+ const INFO: drm::DriverInfo = INFO;
+ const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor] = &[];
+ }
+
+ fn create_drm_dev() -> Result<(faux::Registration, UnregisteredDevice<KunitDriver>)> {
+ // Create a faux DRM device so we can test gem object creation.
+ let data = try_pin_init!(KunitData {});
+ let dev = faux::Registration::new(c"Kunit", None)?;
+ let drm = UnregisteredDevice::new(dev.as_ref(), data)?;
+
+ Ok((dev, drm))
+ }
+
+ #[test]
+ fn compile_time_vmap_sizes() -> Result {
+ let (_dev, drm) = create_drm_dev()?;
+
+ let obj = Object::<KunitObject, _>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?;
+
+ // Try creating a normal vmap
+ obj.vmap::<PAGE_SIZE>()?;
+
+ // Try creating a vmap that's smaller then the size we specified
+ let vmap = obj.vmap::<{ PAGE_SIZE - 100 }>()?;
+
+ // Verify the owner matches
+ assert!(ptr::eq(vmap.owner(), obj.deref()));
+
+ // Verify the max size matches the actual object size
+ assert_eq!(vmap.maxsize(), PAGE_SIZE);
+
+ // Make sure creating a vmap that's too large fails
+ assert!(obj.vmap::<{ PAGE_SIZE + 200 }>().is_err());
+
+ Ok(())
+ }
+
+ #[test]
+ fn vmap_io() -> Result {
+ let (_dev, drm) = create_drm_dev()?;
+
+ let obj = Object::<KunitObject, _>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?;
+
+ let vmap = obj.vmap::<PAGE_SIZE>()?;
+
+ vmap.write8(0xDE, 0x0);
+ assert_eq!(vmap.read8(0x0), 0xDE);
+ vmap.write32(0xFEDCBA98, 0x20);
+
+ assert_eq!(vmap.read32(0x20), 0xFEDCBA98);
+
+ // Ensure the ordering in memory is correct
+ let offsets = (0x20..0x23).into_iter();
+ let expected = 0xFEDCBA98_u32.to_ne_bytes().into_iter();
+ for (offset, expected) in offsets.zip(expected) {
+ assert_eq!(vmap.read8(offset), expected);
+ }
+
+ Ok(())
+ }
+}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v20 2/4] rust: drm: gem: shmem: Add vmap functions
@ 2026-06-10 16:21 ` Lyude Paul
0 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Maxime Ripard, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
One of the more obvious use cases for gem shmem objects is the ability to
create mappings into their contents. So, let's hook this up in our rust
bindings.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
---
V7:
* Switch over to the new iosys map bindings that use the Io trait
V8:
* Get rid of iosys_map bindings for now, only support non-iomem types
* s/as_shmem()/as_raw_shmem()
V9:
* Get rid of some outdated comments I missed
* Add missing SIZE check to raw_vmap()
* Add a proper unit test that ensures that we actually validate SIZE at
compile-time.
Turns out it takes only 34 lines to make a boilerplate DRM driver for a
kunit test :)
* Add unit tests
* Add some missing #[inline]s
V10:
* Correct issue with iomem error path
We previously called raw_vunmap() if we got an iomem allocation, but
raw_vunmap() was written such that it assumed all allocations were sysmem
allocations. Fix this by just making raw_vunmap() accept a iosys_map.
V11:
* Use Alexandre's clever solution to remove the macros we were using for
maintaining two different VMap types.
* Change the order of items in Object<T> to ensure that sgt_res is always
dropped before obj.
* Fix typo in Object.raw_vmap()
* s/raw_vmap()/make_vmap()/
Deduplicate code a bit more as well by using more generics here
V15:
* Add these patches back
* We only have one VMap type now!
* Use ObjectConfig::default() in unit tests since we unbroke it.
V16:
* Fix huge rebase error I made and did not notice that squashed 1.5 patches
together that were definitely not supposed to be squashed
* Update old commit message
V17:
* Rebase
* Fix format of commit message title
V19:
* Drop outdated safety comment
* Move impl_vmap_io_capable! definition to right before it gets used
* Add missing `` in rustdoc for VMap type
* Add a bunch of missing `` in make_vmap()
* Remove one outdated safety comment about reading vaddr_iomem
* Add some missing periods in safety comments in make_vmap().
* Use read_volatile/write_volatile() instead of read()/write() to prevent
compiler reordering.
* Remove impl argument from impl_vmap_io_capable!()
* Check .owner() and .maxsize() in compile_time_vmap_sizes()
* Use more varied pattern in vmap_io()
V20:
* Add missing Send/Sync implementations for VMap
* Use #[inline] not #[inline(always)]
* Add missing invariant comment to VMap instantiation
* Make sure that kunit test doesn't fail on big endian
rust/kernel/drm/gem/shmem.rs | 337 ++++++++++++++++++++++++++++++++++-
1 file changed, 336 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
index 090c5d869fdb7..68a1ce3330b11 100644
--- a/rust/kernel/drm/gem/shmem.rs
+++ b/rust/kernel/drm/gem/shmem.rs
@@ -20,6 +20,11 @@
Registered, //
},
error::to_result,
+ io::{
+ Io,
+ IoCapable,
+ IoKnownSize, //
+ },
prelude::*,
sync::aref::ARef,
types::{
@@ -28,7 +33,9 @@
},
};
use core::{
+ ffi::c_void,
marker::PhantomData,
+ mem::MaybeUninit, //
ops::{
Deref,
DerefMut, //
@@ -39,6 +46,7 @@
},
};
use gem::{
+ BaseObject,
BaseObjectPrivate,
DriverObject,
IntoGEMObject, //
@@ -200,6 +208,79 @@ extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
// SAFETY: We're recovering the Kbox<> we created in gem_create_object()
let _ = unsafe { KBox::from_raw(this) };
}
+
+ /// Attempt to create a vmap from the gem object, and confirm the size of said vmap.
+ fn make_vmap<'a, R, const SIZE: usize>(&'a self) -> Result<VMap<T, R, C, SIZE>>
+ where
+ R: Deref<Target = Self> + From<&'a Self>,
+ {
+ // INVARIANT: We check here that the gem object is at least as large as `SIZE`.
+ if self.size() < SIZE {
+ return Err(ENOSPC);
+ }
+
+ let mut map: MaybeUninit<bindings::iosys_map> = MaybeUninit::uninit();
+ let guard = DmaResvGuard::new(self);
+
+ // SAFETY: `drm_gem_shmem_vmap()` can be called with the DMA reservation lock held.
+ to_result(unsafe {
+ bindings::drm_gem_shmem_vmap_locked(self.as_raw_shmem(), map.as_mut_ptr())
+ })?;
+
+ // Drop the guard explicitly here, since we may need to call `raw_vunmap()` (which
+ // re-acquires the lock).
+ drop(guard);
+
+ // SAFETY: The call to `drm_gem_shmem_vmap_locked()` succeeded above, so we are guaranteed
+ // that map is properly initialized.
+ let map = unsafe { map.assume_init() };
+
+ // XXX: We don't currently support iomem allocations
+ if map.is_iomem {
+ // SAFETY: The vmap operation above succeeded, guaranteeing that `map` points to a valid
+ // memory mapping.
+ unsafe { self.raw_vunmap(map) };
+
+ Err(ENOTSUPP)
+ } else {
+ Ok(VMap {
+ // INVARIANT: `addr` remains valid for as long as `owner` does, which extends to the
+ // lifetime of `VMap` itself.
+ // SAFETY: We checked that this is not an iomem allocation, making it safe to read
+ // vaddr.
+ addr: unsafe { map.__bindgen_anon_1.vaddr },
+ owner: self.into(),
+ })
+ }
+ }
+
+ /// Unmap a vmap from the gem object.
+ ///
+ /// # Safety
+ ///
+ /// - The caller promises that `map` is a valid 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(&self, mut map: bindings::iosys_map) {
+ let _guard = DmaResvGuard::new(self);
+
+ // SAFETY:
+ // - This function is safe to call with the DMA reservation lock held.
+ // - The caller promises that `map` is a valid vmap on this gem object.
+ unsafe { bindings::drm_gem_shmem_vunmap_locked(self.as_raw_shmem(), &mut map) };
+ }
+
+ /// Creates and returns a virtual kernel memory mapping for this object.
+ #[inline]
+ pub fn vmap<const SIZE: usize>(&self) -> Result<VMapRef<'_, T, C, SIZE>> {
+ self.make_vmap()
+ }
+
+ /// Creates and returns an owned reference to a virtual kernel memory mapping for this object.
+ #[inline]
+ pub fn owned_vmap<const SIZE: usize>(&self) -> Result<VMapOwned<T, C, SIZE>> {
+ self.make_vmap()
+ }
}
impl<T: DriverObject, C: DeviceContext> Deref for Object<T, C> {
@@ -263,7 +344,6 @@ struct DmaResvGuard<'a, T: DriverObject, C: DeviceContext = Registered>(
impl<'a, T: DriverObject, C: DeviceContext> DmaResvGuard<'a, T, C> {
#[inline]
- #[expect(unused)]
fn new(obj: &'a Object<T, C>) -> Self {
// SAFETY: This lock is initialized throughout the lifetime of `object`.
unsafe { bindings::dma_resv_lock(obj.raw_dma_resv(), ptr::null_mut()) };
@@ -279,3 +359,258 @@ fn drop(&mut self) {
unsafe { bindings::dma_resv_unlock(self.0.raw_dma_resv()) };
}
}
+
+/// A reference to a virtual mapping for an shmem-based GEM object in kernel address space.
+///
+/// # Invariants
+///
+/// - The size of `owner` is >= SIZE.
+/// - The memory pointed to by `addr` remains valid at least until this object is dropped.
+pub struct VMap<D, R, C = Registered, const SIZE: usize = 0>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ addr: *mut c_void,
+ owner: R,
+}
+
+/// An alias type for a reference to a shmem-based GEM object's VMap.
+pub type VMapRef<'a, D, C, const SIZE: usize = 0> = VMap<D, &'a Object<D, C>, C, SIZE>;
+
+/// An alias type for an owned reference to a shmem-based GEM object's VMap.
+pub type VMapOwned<D, C, const SIZE: usize = 0> = VMap<D, ARef<Object<D, C>>, C, SIZE>;
+
+impl<D, R, C, const SIZE: usize> VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ /// Borrows a reference to the object that owns this virtual mapping.
+ #[inline]
+ pub fn owner(&self) -> &Object<D, C> {
+ &self.owner
+ }
+}
+
+impl<D, R, C, const SIZE: usize> Drop for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY:
+ // - Our existence is proof that this map was previously created using self.owner.
+ // - Since we are in Drop, we are guaranteed that no one will access the memory
+ // through this mapping after calling this.
+ unsafe {
+ self.owner.raw_vunmap(bindings::iosys_map {
+ is_iomem: false,
+ __bindgen_anon_1: bindings::iosys_map__bindgen_ty_1 { vaddr: self.addr },
+ })
+ };
+ }
+}
+
+// SAFETY: VMaps are safe to send across threads.
+unsafe impl<D, R, C, const SIZE: usize> Send for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+}
+
+// SAFETY: VMaps are safe to access from multiple threads.
+unsafe impl<D, R, C, const SIZE: usize> Sync for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+}
+
+impl<D, R, C, const SIZE: usize> Io for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ #[inline]
+ fn addr(&self) -> usize {
+ self.addr as usize
+ }
+
+ #[inline]
+ fn maxsize(&self) -> usize {
+ self.owner.size()
+ }
+}
+
+impl<D, R, C, const SIZE: usize> IoKnownSize for VMap<D, R, C, SIZE>
+where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+{
+ const MIN_SIZE: usize = SIZE;
+}
+
+macro_rules! impl_vmap_io_capable {
+ ($ty:ty) => {
+ impl<D, R, C, const SIZE: usize> IoCapable<$ty> for VMap<D, R, C, SIZE>
+ where
+ D: DriverObject,
+ C: DeviceContext,
+ R: Deref<Target = Object<D, C>>,
+ {
+ #[inline]
+ unsafe fn io_read(&self, address: usize) -> $ty {
+ let ptr = address as *mut $ty;
+
+ // SAFETY: The safety contract of `io_read` guarantees that address is a valid
+ // address within the bounds of `Self` of at least the size of $ty, and is properly
+ // aligned.
+ unsafe { ptr::read_volatile(ptr) }
+ }
+
+ #[inline]
+ unsafe fn io_write(&self, value: $ty, address: usize) {
+ let ptr = address as *mut $ty;
+
+ // SAFETY: The safety contract of `io_write` guarantees that address is a valid
+ // address within the bounds of `Self` of at least the size of $ty, and is properly
+ // aligned.
+ unsafe { ptr::write_volatile(ptr, value) }
+ }
+ }
+ };
+}
+
+impl_vmap_io_capable!(u8);
+impl_vmap_io_capable!(u16);
+impl_vmap_io_capable!(u32);
+#[cfg(CONFIG_64BIT)]
+impl_vmap_io_capable!(u64);
+
+#[kunit_tests(rust_drm_gem_shmem)]
+mod tests {
+ use super::*;
+ use crate::{
+ drm::{
+ self,
+ UnregisteredDevice, //
+ },
+ faux,
+ page::PAGE_SIZE, //
+ };
+
+ // The bare minimum needed to create a fake drm driver for kunit
+
+ #[pin_data]
+ struct KunitData {}
+ struct KunitDriver;
+ struct KunitFile;
+ #[pin_data]
+ struct KunitObject {}
+
+ const INFO: drm::DriverInfo = drm::DriverInfo {
+ major: 0,
+ minor: 0,
+ patchlevel: 0,
+ name: c"kunit",
+ desc: c"Kunit",
+ };
+
+ impl drm::file::DriverFile for KunitFile {
+ type Driver = KunitDriver;
+
+ fn open(_dev: &drm::Device<KunitDriver>) -> Result<Pin<KBox<Self>>> {
+ Ok(KBox::new(Self, GFP_KERNEL)?.into())
+ }
+ }
+
+ impl gem::DriverObject for KunitObject {
+ type Driver = KunitDriver;
+ type Args = ();
+
+ fn new<C: DeviceContext>(
+ _dev: &drm::Device<KunitDriver, C>,
+ _size: usize,
+ _args: Self::Args,
+ ) -> impl PinInit<Self, Error> {
+ try_pin_init!(KunitObject {})
+ }
+ }
+
+ #[vtable]
+ impl drm::Driver for KunitDriver {
+ type Data = KunitData;
+ type File = KunitFile;
+ type Object<Ctx: DeviceContext> = Object<KunitObject, Ctx>;
+
+ const INFO: drm::DriverInfo = INFO;
+ const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor] = &[];
+ }
+
+ fn create_drm_dev() -> Result<(faux::Registration, UnregisteredDevice<KunitDriver>)> {
+ // Create a faux DRM device so we can test gem object creation.
+ let data = try_pin_init!(KunitData {});
+ let dev = faux::Registration::new(c"Kunit", None)?;
+ let drm = UnregisteredDevice::new(dev.as_ref(), data)?;
+
+ Ok((dev, drm))
+ }
+
+ #[test]
+ fn compile_time_vmap_sizes() -> Result {
+ let (_dev, drm) = create_drm_dev()?;
+
+ let obj = Object::<KunitObject, _>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?;
+
+ // Try creating a normal vmap
+ obj.vmap::<PAGE_SIZE>()?;
+
+ // Try creating a vmap that's smaller then the size we specified
+ let vmap = obj.vmap::<{ PAGE_SIZE - 100 }>()?;
+
+ // Verify the owner matches
+ assert!(ptr::eq(vmap.owner(), obj.deref()));
+
+ // Verify the max size matches the actual object size
+ assert_eq!(vmap.maxsize(), PAGE_SIZE);
+
+ // Make sure creating a vmap that's too large fails
+ assert!(obj.vmap::<{ PAGE_SIZE + 200 }>().is_err());
+
+ Ok(())
+ }
+
+ #[test]
+ fn vmap_io() -> Result {
+ let (_dev, drm) = create_drm_dev()?;
+
+ let obj = Object::<KunitObject, _>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?;
+
+ let vmap = obj.vmap::<PAGE_SIZE>()?;
+
+ vmap.write8(0xDE, 0x0);
+ assert_eq!(vmap.read8(0x0), 0xDE);
+ vmap.write32(0xFEDCBA98, 0x20);
+
+ assert_eq!(vmap.read32(0x20), 0xFEDCBA98);
+
+ // Ensure the ordering in memory is correct
+ let offsets = (0x20..0x23).into_iter();
+ let expected = 0xFEDCBA98_u32.to_ne_bytes().into_iter();
+ for (offset, expected) in offsets.zip(expected) {
+ assert_eq!(vmap.read8(offset), expected);
+ }
+
+ Ok(())
+ }
+}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v20 3/4] rust: faux: Allow retrieving a bound Device
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-10 16:21 ` Lyude Paul
-1 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Thomas Zimmermann, Maxime Ripard, David Airlie, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Lyude Paul, Greg Kroah-Hartman
When writing up some rust code that used faux devices for unit testing, I
noticed that we never actually added the Bound device context to
faux::Registration's AsRef<device::Device> implementation. This being said:
the Registration object itself is proof that a driver is bound to the
device - so this should be safe.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
---
V18:
- Add notes from Danilo to safety comment.
rust/kernel/faux.rs | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/rust/kernel/faux.rs b/rust/kernel/faux.rs
index 43b4974f48cd2..20ab638885354 100644
--- a/rust/kernel/faux.rs
+++ b/rust/kernel/faux.rs
@@ -25,7 +25,8 @@
///
/// # Invariants
///
-/// `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
+/// - `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
+/// - This object is proof that the object described by this `Registration` is bound to a device.
///
/// [`struct faux_device`]: srctree/include/linux/device/faux.h
pub struct Registration(NonNull<bindings::faux_device>);
@@ -59,10 +60,15 @@ fn as_raw(&self) -> *mut bindings::faux_device {
}
}
-impl AsRef<device::Device> for Registration {
- fn as_ref(&self) -> &device::Device {
- // SAFETY: The underlying `device` in `faux_device` is guaranteed by the C API to be
- // a valid initialized `device`.
+impl AsRef<device::Device<device::Bound>> for Registration {
+ fn as_ref(&self) -> &device::Device<device::Bound> {
+ // SAFETY:
+ // - The underlying `device` in `faux_device` is guaranteed by the C API to be a valid
+ // initialized `device`.
+ // - faux_match() always returns 1, and probe runs synchronously (PROBE_FORCE_SYNCHRONOUS).
+ // - suppress_bind_attrs = true on faux_driver prevents userspace-triggered unbind via sysfs
+ // - mem::forget(Registration) is not a problem; if the Registration is leaked, the faux
+ // device stays bound forever.
unsafe { device::Device::from_raw(addr_of_mut!((*self.as_raw()).dev)) }
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v20 3/4] rust: faux: Allow retrieving a bound Device
@ 2026-06-10 16:21 ` Lyude Paul
0 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Maxime Ripard, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
When writing up some rust code that used faux devices for unit testing, I
noticed that we never actually added the Bound device context to
faux::Registration's AsRef<device::Device> implementation. This being said:
the Registration object itself is proof that a driver is bound to the
device - so this should be safe.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
---
V18:
- Add notes from Danilo to safety comment.
rust/kernel/faux.rs | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/rust/kernel/faux.rs b/rust/kernel/faux.rs
index 43b4974f48cd2..20ab638885354 100644
--- a/rust/kernel/faux.rs
+++ b/rust/kernel/faux.rs
@@ -25,7 +25,8 @@
///
/// # Invariants
///
-/// `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
+/// - `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
+/// - This object is proof that the object described by this `Registration` is bound to a device.
///
/// [`struct faux_device`]: srctree/include/linux/device/faux.h
pub struct Registration(NonNull<bindings::faux_device>);
@@ -59,10 +60,15 @@ fn as_raw(&self) -> *mut bindings::faux_device {
}
}
-impl AsRef<device::Device> for Registration {
- fn as_ref(&self) -> &device::Device {
- // SAFETY: The underlying `device` in `faux_device` is guaranteed by the C API to be
- // a valid initialized `device`.
+impl AsRef<device::Device<device::Bound>> for Registration {
+ fn as_ref(&self) -> &device::Device<device::Bound> {
+ // SAFETY:
+ // - The underlying `device` in `faux_device` is guaranteed by the C API to be a valid
+ // initialized `device`.
+ // - faux_match() always returns 1, and probe runs synchronously (PROBE_FORCE_SYNCHRONOUS).
+ // - suppress_bind_attrs = true on faux_driver prevents userspace-triggered unbind via sysfs
+ // - mem::forget(Registration) is not a problem; if the Registration is leaked, the faux
+ // device stays bound forever.
unsafe { device::Device::from_raw(addr_of_mut!((*self.as_raw()).dev)) }
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v20 4/4] rust: drm: gem: Introduce shmem::Object::sg_table()
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-10 16:21 ` Lyude Paul
-1 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Thomas Zimmermann, Maxime Ripard, David Airlie, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Lyude Paul, Greg Kroah-Hartman
In order to do this, we need to be careful to ensure that any interface we
expose for scatterlists ensures that any mappings created from one are
destroyed on driver-unbind. To do this, we introduce a Devres resource into
shmem::Object that we use in order to ensure that we release any SGTable
mappings on driver-unbind.
There's some other slightly unfortunate caveats of this:
* Drivers don't have explicit control at the moment over when unmapping
happens (which is exactly the same as the C side atm, so it might not be
a problem).
* We can't just return `SGTableMap` to the user through an Arc to attempt
to fix the last caveat - because that implies the gem object would need
to hold a reference count to the scatterlist mapping, which just leaves
us with the same problem.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
---
V3:
* Rename OwnedSGTable to shmem::SGTable. Since the current version of the
SGTable abstractions now has a `Owned` and `Borrowed` variant, I think
renaming this to shmem::SGTable makes things less confusing.
We do however, keep the name of owned_sg_table() as-is.
V4:
* Clarify safety comments for SGTable to explain why the object is
thread-safe.
* Rename from SGTableRef to SGTable
V10:
* Use Devres in order to ensure that SGTables are revocable, and are
unmapped on driver-unbind.
V11:
* s/create_sg_table()/get_sg_table()
* Get rid of extraneous `ret = ` in shmem::Object::get_sg_table()
V12:
* Actually move sgt_res in this patch and not the next one
V13:
* Use DmaResvGuard suggestion from Alexander
* Use Alexander's (much better) solution for get_sg_table()
* Use SetOnce instead of UnsafeCell
* s/SGTableRef/SGTableMap
* Fix typo in SGTableMap documentation
* Create fallible constructor for SGTableMap
* Don't reuse dma_resv lock for protecting Object contents, just use Mutex
+ SetOnce
* Drop use of drm_gem_shmem_get_pages_sgt_locked(), since we don't need to
hold the dma_resv lock ourselves for anything but this function.
* Check that the device we receive in the bounds for sg_table() and
owned_sg_table() that said Device is in fact, the correct device.
* Remove redundant docs in owned_sg_table(), just point it back to
sg_table().
* Implement Deborah's suggestion to fix double-free in
free_callback()
* Restore original order of Object<T>
* Fix doc typo for SGTableMap
V14:
* Use new InitOnce container over the Mutex/SetOnce horror show we had
before.
* Start using LazyInit container for storing Devres for sgt unmap
* Add some kunit tests for sg_table (not sure why I didn't do this before)
using some of the boilerplate code leftover from the vmap bindings
* Get rid of the owned SGTable variant for now, we'll add it back in a
future patch if people actually need it.
* Use new LazyInit container from me to get rid of the horrid
Mutex<SetOnce<>> mess.
* Add the best we can do for unit tests w/r/t SGTable at the moment
V16:
* Get rid of LazyInit, go back to SetOnce, use trick that Alice recommended
that is a lot cleaner.
* Fix horrid rebasing mistake
V17:
* Rebase
* Fix missing safety comment in free_callback() (we forgot to justify why
&mut is safe in `unsafe { &mut (*this).sgt_res }.reset()`)
V18:
* Use ManuallyDrop instead of SetOnce::reset()
V19:
* Explain that populate() will always return true in sg_table()
* Fix outdated comment in SGTableMap
* Fix invariant comment in SGTableMap
rust/kernel/drm/gem/shmem.rs | 174 +++++++++++++++++++++++++++++++++--
1 file changed, 164 insertions(+), 10 deletions(-)
diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
index 68a1ce3330b11..9dcdab5f78cd4 100644
--- a/rust/kernel/drm/gem/shmem.rs
+++ b/rust/kernel/drm/gem/shmem.rs
@@ -11,6 +11,11 @@
use crate::{
container_of,
+ device::{
+ self,
+ Bound, //
+ },
+ devres::*,
drm::{
driver,
gem,
@@ -19,14 +24,23 @@
DeviceContext,
Registered, //
},
- error::to_result,
+ error::{
+ from_err_ptr,
+ to_result, //
+ },
io::{
Io,
IoCapable,
IoKnownSize, //
},
prelude::*,
- sync::aref::ARef,
+ scatterlist,
+ sync::{
+ aref::ARef,
+ new_mutex,
+ Mutex,
+ SetOnce, //
+ },
types::{
NotThreadSafe,
Opaque, //
@@ -35,7 +49,10 @@
use core::{
ffi::c_void,
marker::PhantomData,
- mem::MaybeUninit, //
+ mem::{
+ ManuallyDrop,
+ MaybeUninit, //
+ },
ops::{
Deref,
DerefMut, //
@@ -90,6 +107,11 @@ pub struct Object<T: DriverObject, C: DeviceContext = Registered> {
obj: Opaque<bindings::drm_gem_shmem_object>,
/// Parent object that owns this object's DMA reservation object.
parent_resv_obj: Option<ARef<Object<T, C>>>,
+ /// Devres object for unmapping any SGTable on driver-unbind.
+ sgt_res: ManuallyDrop<SetOnce<Devres<SGTableMap<T, C>>>>,
+ #[pin]
+ /// Lock for protecting initialization of `sgt_res`.
+ sgt_lock: Mutex<()>,
#[pin]
inner: T,
_ctx: PhantomData<C>,
@@ -148,6 +170,8 @@ pub fn new(
try_pin_init!(Self {
obj <- Opaque::init_zeroed(),
parent_resv_obj: config.parent_resv_obj.map(|p| p.into()),
+ sgt_res: ManuallyDrop::new(SetOnce::new()),
+ sgt_lock <- new_mutex!(()),
inner <- T::new(dev, size, args),
_ctx: PhantomData::<C>,
}),
@@ -192,18 +216,26 @@ extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
// - DRM always passes a valid gem object here
// - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that
// `obj` is contained within a drm_gem_shmem_object
- let this = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) };
-
- // SAFETY:
- // - We're in free_callback - so this function is safe to call.
- // - We won't be using the gem resources on `this` after this call.
- unsafe { bindings::drm_gem_shmem_release(this) };
+ let base = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) };
// SAFETY:
// - We verified above that `obj` is valid, which makes `this` valid
// - This function is set in AllocOps, so we know that `this` is contained within a
// `Object<T, C>`
- let this = unsafe { container_of!(Opaque::cast_from(this), Self, obj) }.cast_mut();
+ let this = unsafe { container_of!(Opaque::cast_from(base), Self, obj) }.cast_mut();
+
+ // We need to drop `sgt_res` first, since doing so requires that the GEM object is still
+ // alive.
+ // SAFETY:
+ // - We verified above that `this` is valid.
+ // - We are in free_callback, guaranteeing we have exclusive access to `this` and that
+ // `sgt_res` will not be used after dropping it here.
+ unsafe { ManuallyDrop::drop(&mut (*this).sgt_res) };
+
+ // SAFETY:
+ // - We're in free_callback - so this function is safe to call.
+ // - We won't be using the gem resources on `this` after this call.
+ unsafe { bindings::drm_gem_shmem_release(base) };
// SAFETY: We're recovering the Kbox<> we created in gem_create_object()
let _ = unsafe { KBox::from_raw(this) };
@@ -281,6 +313,46 @@ pub fn vmap<const SIZE: usize>(&self) -> Result<VMapRef<'_, T, C, SIZE>> {
pub fn owned_vmap<const SIZE: usize>(&self) -> Result<VMapOwned<T, C, SIZE>> {
self.make_vmap()
}
+
+ /// Creates (if necessary) and returns an immutable reference to a scatter-gather table of DMA
+ /// pages for this object.
+ ///
+ /// This will pin the object in memory. It is expected that `dev` should be a pointer to the
+ /// same [`device::Device`] which `self` belongs to, otherwise this function will return
+ /// `Err(EINVAL)`.
+ pub fn sg_table<'a>(
+ &'a self,
+ dev: &'a device::Device<Bound>,
+ ) -> Result<&'a scatterlist::SGTable> {
+ if dev.as_raw() != self.dev().as_ref().as_raw() {
+ return Err(EINVAL);
+ }
+
+ let sgt_res = 'out: {
+ // Fast path: sgt_res is already initialized
+ if let Some(sgt_res) = self.sgt_res.as_ref() {
+ break 'out sgt_res;
+ }
+
+ // Slow path: Grab the lock and see if we need to initialize sgt_res.
+ let _guard = self.sgt_lock.lock();
+
+ // If someone initialized it while we were waiting, we can exit early.
+ if let Some(sgt_res) = self.sgt_res.as_ref() {
+ break 'out sgt_res;
+ }
+
+ // If not, finish initializing and return. `populate()` cannot return false, as
+ // `sgt_res` must be unpopulated, and we must hold `sgt_lock` to reach this point.
+ self.sgt_res
+ .populate(Devres::new(dev, SGTableMap::new(self))?);
+
+ // SAFETY: We just populated sgt_res above.
+ unsafe { self.sgt_res.as_ref().unwrap_unchecked() }
+ };
+
+ Ok(sgt_res.access(dev)?)
+ }
}
impl<T: DriverObject, C: DeviceContext> Deref for Object<T, C> {
@@ -497,6 +569,64 @@ unsafe fn io_write(&self, value: $ty, address: usize) {
#[cfg(CONFIG_64BIT)]
impl_vmap_io_capable!(u64);
+/// A reference to a GEM object that is known to have a mapped [`SGTable`].
+///
+/// This is used by the Rust bindings with [`Devres`] in order to ensure that mappings for SGTables
+/// on GEM shmem objects are revoked on driver-unbind.
+///
+/// # Invariants
+///
+/// - `self.obj` always points to a valid GEM object.
+/// - This object is proof that `self.obj.owner.sgt_res` has an initialized and valid pointer to an
+/// [`SGTable`].
+///
+/// [`SGTable`]: scatterlist::SGTable
+pub struct SGTableMap<T: DriverObject, C: DeviceContext> {
+ obj: NonNull<Object<T, C>>,
+}
+
+impl<T: DriverObject, C: DeviceContext> Deref for SGTableMap<T, C> {
+ type Target = scatterlist::SGTable;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY:
+ // - The NonNull is guaranteed to be valid via our type invariants.
+ // - The sgt field is guaranteed to be initialized and valid via our type invariants.
+ unsafe { scatterlist::SGTable::from_raw((*self.obj.as_ref().as_raw_shmem()).sgt) }
+ }
+}
+
+impl<T: DriverObject, C: DeviceContext> Drop for SGTableMap<T, C> {
+ fn drop(&mut self) {
+ // SAFETY: `obj` is always valid via our type invariants
+ let obj = unsafe { self.obj.as_ref() };
+ let _lock = DmaResvGuard::new(obj);
+
+ // SAFETY: We acquired the lock needed for calling this function above
+ unsafe { bindings::__drm_gem_shmem_free_sgt_locked(obj.as_raw_shmem()) };
+ }
+}
+
+impl<T: DriverObject, C: DeviceContext> SGTableMap<T, C> {
+ fn new(obj: &Object<T, C>) -> impl Init<Self, Error> {
+ // INVARIANT:
+ // - We call drm_gem_shmem_get_pages_sgt below and check whether or not it succeeds,
+ // fulfilling the invariant of SGTableMap that the object's `sgt` field is initialized.
+ // SAFETY:
+ // - `obj` is fully initialized, making this function safe to call.
+ from_err_ptr(unsafe { bindings::drm_gem_shmem_get_pages_sgt(obj.as_raw_shmem()) })?;
+
+ Ok(Self { obj: obj.into() })
+ }
+}
+
+// SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invariants, and the GEM object
+// it points to is guaranteed to be thread-safe.
+unsafe impl<T: DriverObject, C: DeviceContext> Send for SGTableMap<T, C> {}
+// SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invariants, and the GEM object
+// it points to is guaranteed to be thread-safe.
+unsafe impl<T: DriverObject, C: DeviceContext> Sync for SGTableMap<T, C> {}
+
#[kunit_tests(rust_drm_gem_shmem)]
mod tests {
use super::*;
@@ -613,4 +743,28 @@ fn vmap_io() -> Result {
Ok(())
}
+
+ // TODO: I would love to actually test the success paths of sg_table(), but that would require
+ // also implementing dummy dma_ops so that trying to create a mapping doesn't explode. So, leave
+ // that for someone else.
+
+ // Ensures that passing the wrong device to sg_table() fails as we expect, and also ensure it
+ // skips initializing `sgt_res` since we could otherwise create `sgt_res` with the wrong device
+ // bound to it.
+ #[test]
+ fn fail_sg_table_on_wrong_dev() -> Result {
+ let (_dev, drm) = create_drm_dev()?;
+ let wrong_dev = faux::Registration::new(c"EvilKunit", None)?;
+
+ let obj = Object::<KunitObject, _>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?;
+
+ assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL);
+
+ // If sgt_res was not initialized mistakenly with the wrong device, this should still fail.
+ assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL);
+
+ // TODO: Someday, we should test that creating an sg_table here still succeeds.
+
+ Ok(())
+ }
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v20 4/4] rust: drm: gem: Introduce shmem::Object::sg_table()
@ 2026-06-10 16:21 ` Lyude Paul
0 siblings, 0 replies; 21+ messages in thread
From: Lyude Paul @ 2026-06-10 16:21 UTC (permalink / raw)
To: dri-devel, rust-for-linux, nouveau
Cc: Alexandre Courbot, Gary Guo, Christian König, driver-core,
Miguel Ojeda, Maarten Lankhorst, Alice Ryhl, Simona Vetter,
linux-kernel, Sumit Semwal, linux-media, Rafael J . Wysocki,
Maxime Ripard, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
In order to do this, we need to be careful to ensure that any interface we
expose for scatterlists ensures that any mappings created from one are
destroyed on driver-unbind. To do this, we introduce a Devres resource into
shmem::Object that we use in order to ensure that we release any SGTable
mappings on driver-unbind.
There's some other slightly unfortunate caveats of this:
* Drivers don't have explicit control at the moment over when unmapping
happens (which is exactly the same as the C side atm, so it might not be
a problem).
* We can't just return `SGTableMap` to the user through an Arc to attempt
to fix the last caveat - because that implies the gem object would need
to hold a reference count to the scatterlist mapping, which just leaves
us with the same problem.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
---
V3:
* Rename OwnedSGTable to shmem::SGTable. Since the current version of the
SGTable abstractions now has a `Owned` and `Borrowed` variant, I think
renaming this to shmem::SGTable makes things less confusing.
We do however, keep the name of owned_sg_table() as-is.
V4:
* Clarify safety comments for SGTable to explain why the object is
thread-safe.
* Rename from SGTableRef to SGTable
V10:
* Use Devres in order to ensure that SGTables are revocable, and are
unmapped on driver-unbind.
V11:
* s/create_sg_table()/get_sg_table()
* Get rid of extraneous `ret = ` in shmem::Object::get_sg_table()
V12:
* Actually move sgt_res in this patch and not the next one
V13:
* Use DmaResvGuard suggestion from Alexander
* Use Alexander's (much better) solution for get_sg_table()
* Use SetOnce instead of UnsafeCell
* s/SGTableRef/SGTableMap
* Fix typo in SGTableMap documentation
* Create fallible constructor for SGTableMap
* Don't reuse dma_resv lock for protecting Object contents, just use Mutex
+ SetOnce
* Drop use of drm_gem_shmem_get_pages_sgt_locked(), since we don't need to
hold the dma_resv lock ourselves for anything but this function.
* Check that the device we receive in the bounds for sg_table() and
owned_sg_table() that said Device is in fact, the correct device.
* Remove redundant docs in owned_sg_table(), just point it back to
sg_table().
* Implement Deborah's suggestion to fix double-free in
free_callback()
* Restore original order of Object<T>
* Fix doc typo for SGTableMap
V14:
* Use new InitOnce container over the Mutex/SetOnce horror show we had
before.
* Start using LazyInit container for storing Devres for sgt unmap
* Add some kunit tests for sg_table (not sure why I didn't do this before)
using some of the boilerplate code leftover from the vmap bindings
* Get rid of the owned SGTable variant for now, we'll add it back in a
future patch if people actually need it.
* Use new LazyInit container from me to get rid of the horrid
Mutex<SetOnce<>> mess.
* Add the best we can do for unit tests w/r/t SGTable at the moment
V16:
* Get rid of LazyInit, go back to SetOnce, use trick that Alice recommended
that is a lot cleaner.
* Fix horrid rebasing mistake
V17:
* Rebase
* Fix missing safety comment in free_callback() (we forgot to justify why
&mut is safe in `unsafe { &mut (*this).sgt_res }.reset()`)
V18:
* Use ManuallyDrop instead of SetOnce::reset()
V19:
* Explain that populate() will always return true in sg_table()
* Fix outdated comment in SGTableMap
* Fix invariant comment in SGTableMap
rust/kernel/drm/gem/shmem.rs | 174 +++++++++++++++++++++++++++++++++--
1 file changed, 164 insertions(+), 10 deletions(-)
diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
index 68a1ce3330b11..9dcdab5f78cd4 100644
--- a/rust/kernel/drm/gem/shmem.rs
+++ b/rust/kernel/drm/gem/shmem.rs
@@ -11,6 +11,11 @@
use crate::{
container_of,
+ device::{
+ self,
+ Bound, //
+ },
+ devres::*,
drm::{
driver,
gem,
@@ -19,14 +24,23 @@
DeviceContext,
Registered, //
},
- error::to_result,
+ error::{
+ from_err_ptr,
+ to_result, //
+ },
io::{
Io,
IoCapable,
IoKnownSize, //
},
prelude::*,
- sync::aref::ARef,
+ scatterlist,
+ sync::{
+ aref::ARef,
+ new_mutex,
+ Mutex,
+ SetOnce, //
+ },
types::{
NotThreadSafe,
Opaque, //
@@ -35,7 +49,10 @@
use core::{
ffi::c_void,
marker::PhantomData,
- mem::MaybeUninit, //
+ mem::{
+ ManuallyDrop,
+ MaybeUninit, //
+ },
ops::{
Deref,
DerefMut, //
@@ -90,6 +107,11 @@ pub struct Object<T: DriverObject, C: DeviceContext = Registered> {
obj: Opaque<bindings::drm_gem_shmem_object>,
/// Parent object that owns this object's DMA reservation object.
parent_resv_obj: Option<ARef<Object<T, C>>>,
+ /// Devres object for unmapping any SGTable on driver-unbind.
+ sgt_res: ManuallyDrop<SetOnce<Devres<SGTableMap<T, C>>>>,
+ #[pin]
+ /// Lock for protecting initialization of `sgt_res`.
+ sgt_lock: Mutex<()>,
#[pin]
inner: T,
_ctx: PhantomData<C>,
@@ -148,6 +170,8 @@ pub fn new(
try_pin_init!(Self {
obj <- Opaque::init_zeroed(),
parent_resv_obj: config.parent_resv_obj.map(|p| p.into()),
+ sgt_res: ManuallyDrop::new(SetOnce::new()),
+ sgt_lock <- new_mutex!(()),
inner <- T::new(dev, size, args),
_ctx: PhantomData::<C>,
}),
@@ -192,18 +216,26 @@ extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
// - DRM always passes a valid gem object here
// - We used drm_gem_shmem_create() in our create_gem_object callback, so we know that
// `obj` is contained within a drm_gem_shmem_object
- let this = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) };
-
- // SAFETY:
- // - We're in free_callback - so this function is safe to call.
- // - We won't be using the gem resources on `this` after this call.
- unsafe { bindings::drm_gem_shmem_release(this) };
+ let base = unsafe { container_of!(obj, bindings::drm_gem_shmem_object, base) };
// SAFETY:
// - We verified above that `obj` is valid, which makes `this` valid
// - This function is set in AllocOps, so we know that `this` is contained within a
// `Object<T, C>`
- let this = unsafe { container_of!(Opaque::cast_from(this), Self, obj) }.cast_mut();
+ let this = unsafe { container_of!(Opaque::cast_from(base), Self, obj) }.cast_mut();
+
+ // We need to drop `sgt_res` first, since doing so requires that the GEM object is still
+ // alive.
+ // SAFETY:
+ // - We verified above that `this` is valid.
+ // - We are in free_callback, guaranteeing we have exclusive access to `this` and that
+ // `sgt_res` will not be used after dropping it here.
+ unsafe { ManuallyDrop::drop(&mut (*this).sgt_res) };
+
+ // SAFETY:
+ // - We're in free_callback - so this function is safe to call.
+ // - We won't be using the gem resources on `this` after this call.
+ unsafe { bindings::drm_gem_shmem_release(base) };
// SAFETY: We're recovering the Kbox<> we created in gem_create_object()
let _ = unsafe { KBox::from_raw(this) };
@@ -281,6 +313,46 @@ pub fn vmap<const SIZE: usize>(&self) -> Result<VMapRef<'_, T, C, SIZE>> {
pub fn owned_vmap<const SIZE: usize>(&self) -> Result<VMapOwned<T, C, SIZE>> {
self.make_vmap()
}
+
+ /// Creates (if necessary) and returns an immutable reference to a scatter-gather table of DMA
+ /// pages for this object.
+ ///
+ /// This will pin the object in memory. It is expected that `dev` should be a pointer to the
+ /// same [`device::Device`] which `self` belongs to, otherwise this function will return
+ /// `Err(EINVAL)`.
+ pub fn sg_table<'a>(
+ &'a self,
+ dev: &'a device::Device<Bound>,
+ ) -> Result<&'a scatterlist::SGTable> {
+ if dev.as_raw() != self.dev().as_ref().as_raw() {
+ return Err(EINVAL);
+ }
+
+ let sgt_res = 'out: {
+ // Fast path: sgt_res is already initialized
+ if let Some(sgt_res) = self.sgt_res.as_ref() {
+ break 'out sgt_res;
+ }
+
+ // Slow path: Grab the lock and see if we need to initialize sgt_res.
+ let _guard = self.sgt_lock.lock();
+
+ // If someone initialized it while we were waiting, we can exit early.
+ if let Some(sgt_res) = self.sgt_res.as_ref() {
+ break 'out sgt_res;
+ }
+
+ // If not, finish initializing and return. `populate()` cannot return false, as
+ // `sgt_res` must be unpopulated, and we must hold `sgt_lock` to reach this point.
+ self.sgt_res
+ .populate(Devres::new(dev, SGTableMap::new(self))?);
+
+ // SAFETY: We just populated sgt_res above.
+ unsafe { self.sgt_res.as_ref().unwrap_unchecked() }
+ };
+
+ Ok(sgt_res.access(dev)?)
+ }
}
impl<T: DriverObject, C: DeviceContext> Deref for Object<T, C> {
@@ -497,6 +569,64 @@ unsafe fn io_write(&self, value: $ty, address: usize) {
#[cfg(CONFIG_64BIT)]
impl_vmap_io_capable!(u64);
+/// A reference to a GEM object that is known to have a mapped [`SGTable`].
+///
+/// This is used by the Rust bindings with [`Devres`] in order to ensure that mappings for SGTables
+/// on GEM shmem objects are revoked on driver-unbind.
+///
+/// # Invariants
+///
+/// - `self.obj` always points to a valid GEM object.
+/// - This object is proof that `self.obj.owner.sgt_res` has an initialized and valid pointer to an
+/// [`SGTable`].
+///
+/// [`SGTable`]: scatterlist::SGTable
+pub struct SGTableMap<T: DriverObject, C: DeviceContext> {
+ obj: NonNull<Object<T, C>>,
+}
+
+impl<T: DriverObject, C: DeviceContext> Deref for SGTableMap<T, C> {
+ type Target = scatterlist::SGTable;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY:
+ // - The NonNull is guaranteed to be valid via our type invariants.
+ // - The sgt field is guaranteed to be initialized and valid via our type invariants.
+ unsafe { scatterlist::SGTable::from_raw((*self.obj.as_ref().as_raw_shmem()).sgt) }
+ }
+}
+
+impl<T: DriverObject, C: DeviceContext> Drop for SGTableMap<T, C> {
+ fn drop(&mut self) {
+ // SAFETY: `obj` is always valid via our type invariants
+ let obj = unsafe { self.obj.as_ref() };
+ let _lock = DmaResvGuard::new(obj);
+
+ // SAFETY: We acquired the lock needed for calling this function above
+ unsafe { bindings::__drm_gem_shmem_free_sgt_locked(obj.as_raw_shmem()) };
+ }
+}
+
+impl<T: DriverObject, C: DeviceContext> SGTableMap<T, C> {
+ fn new(obj: &Object<T, C>) -> impl Init<Self, Error> {
+ // INVARIANT:
+ // - We call drm_gem_shmem_get_pages_sgt below and check whether or not it succeeds,
+ // fulfilling the invariant of SGTableMap that the object's `sgt` field is initialized.
+ // SAFETY:
+ // - `obj` is fully initialized, making this function safe to call.
+ from_err_ptr(unsafe { bindings::drm_gem_shmem_get_pages_sgt(obj.as_raw_shmem()) })?;
+
+ Ok(Self { obj: obj.into() })
+ }
+}
+
+// SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invariants, and the GEM object
+// it points to is guaranteed to be thread-safe.
+unsafe impl<T: DriverObject, C: DeviceContext> Send for SGTableMap<T, C> {}
+// SAFETY: The NonNull in SGTableMap is guaranteed valid by our type invariants, and the GEM object
+// it points to is guaranteed to be thread-safe.
+unsafe impl<T: DriverObject, C: DeviceContext> Sync for SGTableMap<T, C> {}
+
#[kunit_tests(rust_drm_gem_shmem)]
mod tests {
use super::*;
@@ -613,4 +743,28 @@ fn vmap_io() -> Result {
Ok(())
}
+
+ // TODO: I would love to actually test the success paths of sg_table(), but that would require
+ // also implementing dummy dma_ops so that trying to create a mapping doesn't explode. So, leave
+ // that for someone else.
+
+ // Ensures that passing the wrong device to sg_table() fails as we expect, and also ensure it
+ // skips initializing `sgt_res` since we could otherwise create `sgt_res` with the wrong device
+ // bound to it.
+ #[test]
+ fn fail_sg_table_on_wrong_dev() -> Result {
+ let (_dev, drm) = create_drm_dev()?;
+ let wrong_dev = faux::Registration::new(c"EvilKunit", None)?;
+
+ let obj = Object::<KunitObject, _>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?;
+
+ assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL);
+
+ // If sgt_res was not initialized mistakenly with the wrong device, this should still fail.
+ assert_eq!(obj.sg_table(wrong_dev.as_ref()).err().unwrap(), EINVAL);
+
+ // TODO: Someday, we should test that creating an sg_table here still succeeds.
+
+ Ok(())
+ }
}
--
2.54.0
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v20 3/4] rust: faux: Allow retrieving a bound Device
2026-06-10 16:21 ` Lyude Paul
(?)
@ 2026-06-10 16:37 ` sashiko-bot
-1 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-06-10 16:37 UTC (permalink / raw)
To: Lyude Paul; +Cc: dri-devel, ojeda
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] The newly added invariant comment incorrectly states the faux device is 'bound to a device' instead of 'bound to a driver'.
- [Low] Missing `// INVARIANT:` comment justification for the newly added type invariant in the constructor `Registration::new()`.
--
commit 4f6cdb361b5df7689eb3f97b5d9628c28576af9e
Author: Lyude Paul <lyude@redhat.com>
rust: faux: Allow retrieving a bound Device
This commit allows retrieving a Bound device context from a faux::Registration
by implementing AsRef<device::Device<device::Bound>>. It also adds a new type
invariant stating that the Registration object proves the device is bound.
> diff --git a/rust/kernel/faux.rs b/rust/kernel/faux.rs
> index 43b4974f48cd2..20ab638885354 100644
> --- a/rust/kernel/faux.rs
> +++ b/rust/kernel/faux.rs
> @@ -25,7 +25,8 @@
> ///
> /// # Invariants
> ///
> -/// `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
> +/// - `self.0` always holds a valid pointer to an initialized and registered [`struct faux_device`].
> +/// - This object is proof that the object described by this `Registration` is bound to a device.
[Severity: Low]
This isn't a bug, but should this say "bound to a driver" instead of "bound to
a device"? A faux device is bound to a driver, not another device.
[Severity: Low]
Since a new invariant was added here, does Registration::new() need an updated
// INVARIANT: comment to justify it?
According to the subsystem guidelines, when a struct with an # Invariants
section is constructed, there must be a corresponding // INVARIANT: comment.
The current constructor only justifies the first invariant:
rust/kernel/faux.rs:Registration::new() {
...
// The above function will return either a valid device, or NULL on failure
// INVARIANT: The device will remain registered until faux_device_destroy() is called, which
// happens in our Drop implementation.
Ok(Self(NonNull::new(dev).ok_or(ENODEV)?))
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260610162433.923550-1-lyude@redhat.com?part=3
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 2/4] rust: drm: gem: shmem: Add vmap functions
2026-06-10 16:21 ` Lyude Paul
(?)
@ 2026-06-10 16:39 ` sashiko-bot
-1 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-06-10 16:39 UTC (permalink / raw)
To: Lyude Paul; +Cc: dri-devel, ojeda
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] VMap unconditionally implements Send and Sync without requiring its generic parameter R to be Send or Sync.
- [High] The Sync implementation for VMap combined with its IoCapable trait implementations allows safe concurrent volatile writes to the same memory location, causing a data race.
- [Medium] Off-by-one error in vmap_io kunit test loop prematurely terminates the test, preventing verification of the final byte.
- [Low] Struct VMap fails to compile due to unused generic type parameters D and C.
--
commit 2c1f27a93b4873be7178ca4b1861ef62ac59a6b1
Author: Lyude Paul <lyude@redhat.com>
rust: drm: gem: shmem: Add vmap functions
This commit hooks up vmap creation for gem shmem objects in the Rust bindings,
allowing mappings to be created for their contents.
> diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
> index bfbf2bb25eac1..d4afc06d1be50 100644
> --- a/rust/kernel/drm/gem/shmem.rs
> +++ b/rust/kernel/drm/gem/shmem.rs
[ ... ]
> +/// A reference to a virtual mapping for an shmem-based GEM object in kernel address space.
> +///
> +/// # Invariants
> +///
> +/// - The size of `owner` is >= SIZE.
> +/// - The memory pointed to by `addr` remains valid at least until this object is dropped.
> +pub struct VMap<D, R, C = Registered, const SIZE: usize = 0>
> +where
> + D: DriverObject,
> + C: DeviceContext,
> + R: Deref<Target = Object<D, C>>,
> +{
> + addr: *mut c_void,
> + owner: R,
> +}
[Severity: Low]
Does this struct compile successfully?
The generic type parameters D and C are used in the where clause but not in
any fields. Would this trigger a compiler error (E0392) requiring a
PhantomData field to compute variance?
[ ... ]
> +// SAFETY: VMaps are safe to send across threads.
> +unsafe impl<D, R, C, const SIZE: usize> Send for VMap<D, R, C, SIZE>
> +where
> + D: DriverObject,
> + C: DeviceContext,
> + R: Deref<Target = Object<D, C>>,
> +{
> +}
> +
> +// SAFETY: VMaps are safe to access from multiple threads.
> +unsafe impl<D, R, C, const SIZE: usize> Sync for VMap<D, R, C, SIZE>
> +where
> + D: DriverObject,
> + C: DeviceContext,
> + R: Deref<Target = Object<D, C>>,
> +{
> +}
[Severity: High]
Are these unconditional Send and Sync implementations sound?
If a caller provides a custom type for R that implements Deref but is !Send
or !Sync (such as a type using non-atomic reference counting), VMap would
still be allowed across threads. When the VMap is dropped on another thread,
the !Send R would be dropped there, resulting in a data race.
Should these implementations require explicit Send and Sync bounds on R?
[ ... ]
> +macro_rules! impl_vmap_io_capable {
> + ($ty:ty) => {
> + impl<D, R, C, const SIZE: usize> IoCapable<$ty> for VMap<D, R, C, SIZE>
> + where
> + D: DriverObject,
> + C: DeviceContext,
> + R: Deref<Target = Object<D, C>>,
> + {
> + #[inline]
> + unsafe fn io_read(&self, address: usize) -> $ty {
> + let ptr = address as *mut $ty;
> +
> + // SAFETY: The safety contract of `io_read` guarantees that address is a valid
> + // address within the bounds of `Self` of at least the size of $ty, and is properly
> + // aligned.
> + unsafe { ptr::read_volatile(ptr) }
> + }
> +
> + #[inline]
> + unsafe fn io_write(&self, value: $ty, address: usize) {
> + let ptr = address as *mut $ty;
> +
> + // SAFETY: The safety contract of `io_write` guarantees that address is a valid
> + // address within the bounds of `Self` of at least the size of $ty, and is properly
> + // aligned.
> + unsafe { ptr::write_volatile(ptr, value) }
> + }
> + }
> + };
> +}
[Severity: High]
Can this result in a data race?
Since VMap implements the Sync trait, shared references (&VMap) can be safely
sent to multiple threads. This IoCapable implementation powers safe Io trait
accessors (like write8) using ptr::write_volatile, which is not atomic.
According to the Rust memory model, concurrent volatile writes from multiple
threads to the same memory location constitute a data race. Should this
synchronize access to avoid undefined behavior?
[ ... ]
> + #[test]
> + fn vmap_io() -> Result {
> + let (_dev, drm) = create_drm_dev()?;
> +
> + let obj = Object::<KunitObject, _>::new(&drm, PAGE_SIZE, ObjectConfig::default(), ())?;
> +
> + let vmap = obj.vmap::<PAGE_SIZE>()?;
> +
> + vmap.write8(0xDE, 0x0);
> + assert_eq!(vmap.read8(0x0), 0xDE);
> + vmap.write32(0xFEDCBA98, 0x20);
> +
> + assert_eq!(vmap.read32(0x20), 0xFEDCBA98);
> +
> + // Ensure the ordering in memory is correct
> + let offsets = (0x20..0x23).into_iter();
> + let expected = 0xFEDCBA98_u32.to_ne_bytes().into_iter();
> + for (offset, expected) in offsets.zip(expected) {
> + assert_eq!(vmap.read8(offset), expected);
> + }
> +
> + Ok(())
> + }
> +}
[Severity: Medium]
Does this loop skip the 4th byte verification?
The offsets iterator (0x20..0x23) produces exactly 3 elements (0x20, 0x21,
and 0x22). Because zip stops when the shortest iterator is exhausted, it
appears the 4th byte at offset 0x23 is silently skipped and never verified.
Should this be (0x20..=0x23) or (0x20..0x24) instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260610162433.923550-1-lyude@redhat.com?part=2
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 4/4] rust: drm: gem: Introduce shmem::Object::sg_table()
2026-06-10 16:21 ` Lyude Paul
(?)
@ 2026-06-10 16:46 ` sashiko-bot
-1 siblings, 0 replies; 21+ messages in thread
From: sashiko-bot @ 2026-06-10 16:46 UTC (permalink / raw)
To: Lyude Paul; +Cc: ojeda, dri-devel
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] A concurrent device unbind and GEM object destruction results in a use-after-free and double-free when SGTableMap::drop accesses the freed GEM object.
- [High] SGTableMap::drop unconditionally calls __drm_gem_shmem_free_sgt_locked(), causing NULL pointer dereferences for purged objects, memory corruption for PRIME-imported objects, and leaked page references.
--
commit 8c8b7df9b9589f955da172bd30324598c3ed5764
Author: Lyude Paul <lyude@redhat.com>
rust: drm: gem: Introduce shmem::Object::sg_table()
In order to do this, we need to be careful to ensure that any interface we
expose for scatterlists ensures that any mappings created from one are
destroyed on driver-unbind. To do this, we introduce a Devres resource into
shmem::Object that we use in order to ensure that we release any SGTable
mappings on driver-unbind.
> diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs
> index d4afc06d1be50..f44665cc24119 100644
> --- a/rust/kernel/drm/gem/shmem.rs
> +++ b/rust/kernel/drm/gem/shmem.rs
[ ... ]
> @@ -488,6 +560,64 @@ unsafe fn io_write(&self, value: $ty, address: usize) {
> #[cfg(CONFIG_64BIT)]
> impl_vmap_io_capable!(u64);
>
> +/// A reference to a GEM object that is known to have a mapped [`SGTable`].
> +///
> +/// This is used by the Rust bindings with [`Devres`] in order to ensure that mappings for SGTables
> +/// on GEM shmem objects are revoked on driver-unbind.
> +///
> +/// # Invariants
> +///
> +/// - `self.obj` always points to a valid GEM object.
> +/// - This object is proof that `self.obj.owner.sgt_res` has an initialized and valid pointer to an
> +/// [`SGTable`].
> +///
> +/// [`SGTable`]: scatterlist::SGTable
> +pub struct SGTableMap<T: DriverObject, C: DeviceContext> {
> + obj: NonNull<Object<T, C>>,
> +}
> +
> +impl<T: DriverObject, C: DeviceContext> Deref for SGTableMap<T, C> {
> + type Target = scatterlist::SGTable;
> +
> + fn deref(&self) -> &Self::Target {
> + // SAFETY:
> + // - The NonNull is guaranteed to be valid via our type invariants.
> + // - The sgt field is guaranteed to be initialized and valid via our type invariants.
> + unsafe { scatterlist::SGTable::from_raw((*self.obj.as_ref().as_raw_shmem()).sgt) }
> + }
> +}
> +
> +impl<T: DriverObject, C: DeviceContext> Drop for SGTableMap<T, C> {
> + fn drop(&mut self) {
> + // SAFETY: `obj` is always valid via our type invariants
> + let obj = unsafe { self.obj.as_ref() };
> + let _lock = DmaResvGuard::new(obj);
[Severity: Critical]
Can a concurrent device unbind and GEM object destruction result in a
use-after-free here?
When a GEM object's final reference is dropped, free_callback() is executed
and it drops the SetOnce wrapper:
unsafe { ManuallyDrop::drop(&mut (*this).sgt_res) };
This invokes Devres::drop(), which calls revoke_nosync().
If the device is concurrently unbound, the devres unbind thread will call
revoke() on the Devres object, setting its availability flag to false and
waiting via synchronize_rcu() before dropping SGTableMap.
Because revoke_nosync() on the free_callback thread sees the flag is already
false, it returns immediately without synchronizing. free_callback() then
proceeds to free the GEM object memory via drm_gem_shmem_release().
Once synchronize_rcu() finishes in the unbind thread, this SGTableMap::drop
runs and dereferences the now-freed self.obj pointer, resulting in a
use-after-free.
> +
> + // SAFETY: We acquired the lock needed for calling this function above
> + unsafe { bindings::__drm_gem_shmem_free_sgt_locked(obj.as_raw_shmem()) };
[Severity: High]
Does unconditionally calling __drm_gem_shmem_free_sgt_locked() handle purged
or PRIME-imported objects correctly?
If a Rust Object is PRIME-imported, shmem->sgt points to an SGT owned by the
exporting driver, and calling this could free and corrupt the exporter's
memory.
Additionally, if the object was purged by the shrinker, shmem->sgt is freed
and set to NULL. Calling __drm_gem_shmem_free_sgt_locked() does not check
for NULL, which could lead to a NULL pointer dereference in
dma_unmap_sgtable().
Finally, for native objects, drm_gem_shmem_get_pages_sgt() pins the pages
and increments pages_use_count. By only freeing the SGT and skipping the
required drm_gem_shmem_put_pages_locked() call, does this leak page
references until the object's file descriptor is closed?
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260610162433.923550-1-lyude@redhat.com?part=4
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 1/4] rust: drm: gem: shmem: Add DmaResvGuard helper
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-11 11:07 ` Alice Ryhl
-1 siblings, 0 replies; 21+ messages in thread
From: Alice Ryhl @ 2026-06-11 11:07 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Simona Vetter, linux-kernel, Sumit Semwal,
linux-media, Rafael J . Wysocki, Thomas Zimmermann, Maxime Ripard,
David Airlie, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
On Wed, Jun 10, 2026 at 12:21:28PM -0400, Lyude Paul wrote:
> Just a temporary holdover to make locking/unlocking the dma_resv lock much
> easier.
>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> Co-authored-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 1/4] rust: drm: gem: shmem: Add DmaResvGuard helper
@ 2026-06-11 11:07 ` Alice Ryhl
0 siblings, 0 replies; 21+ messages in thread
From: Alice Ryhl @ 2026-06-11 11:07 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Simona Vetter, linux-kernel, Sumit Semwal,
linux-media, Rafael J . Wysocki, Maxime Ripard, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Greg Kroah-Hartman
On Wed, Jun 10, 2026 at 12:21:28PM -0400, Lyude Paul wrote:
> Just a temporary holdover to make locking/unlocking the dma_resv lock much
> easier.
>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> Co-authored-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 2/4] rust: drm: gem: shmem: Add vmap functions
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-11 11:10 ` Alice Ryhl
-1 siblings, 0 replies; 21+ messages in thread
From: Alice Ryhl @ 2026-06-11 11:10 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Simona Vetter, linux-kernel, Sumit Semwal,
linux-media, Rafael J . Wysocki, Thomas Zimmermann, Maxime Ripard,
David Airlie, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
On Wed, Jun 10, 2026 at 12:21:29PM -0400, Lyude Paul wrote:
> One of the more obvious use cases for gem shmem objects is the ability to
> create mappings into their contents. So, let's hook this up in our rust
> bindings.
>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 2/4] rust: drm: gem: shmem: Add vmap functions
@ 2026-06-11 11:10 ` Alice Ryhl
0 siblings, 0 replies; 21+ messages in thread
From: Alice Ryhl @ 2026-06-11 11:10 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Simona Vetter, linux-kernel, Sumit Semwal,
linux-media, Rafael J . Wysocki, Maxime Ripard, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Greg Kroah-Hartman
On Wed, Jun 10, 2026 at 12:21:29PM -0400, Lyude Paul wrote:
> One of the more obvious use cases for gem shmem objects is the ability to
> create mappings into their contents. So, let's hook this up in our rust
> bindings.
>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 3/4] rust: faux: Allow retrieving a bound Device
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-11 11:10 ` Alice Ryhl
-1 siblings, 0 replies; 21+ messages in thread
From: Alice Ryhl @ 2026-06-11 11:10 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Simona Vetter, linux-kernel, Sumit Semwal,
linux-media, Rafael J . Wysocki, Maxime Ripard, Benno Lossin,
linaro-mm-sig, Danilo Krummrich, Mukesh Kumar Chaurasiya,
Asahi Lina, Daniel Almeida, Greg Kroah-Hartman
On Wed, Jun 10, 2026 at 12:21:30PM -0400, Lyude Paul wrote:
> When writing up some rust code that used faux devices for unit testing, I
> noticed that we never actually added the Bound device context to
> faux::Registration's AsRef<device::Device> implementation. This being said:
> the Registration object itself is proof that a driver is bound to the
> device - so this should be safe.
>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 3/4] rust: faux: Allow retrieving a bound Device
@ 2026-06-11 11:10 ` Alice Ryhl
0 siblings, 0 replies; 21+ messages in thread
From: Alice Ryhl @ 2026-06-11 11:10 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Simona Vetter, linux-kernel, Sumit Semwal,
linux-media, Rafael J . Wysocki, Thomas Zimmermann, Maxime Ripard,
David Airlie, Benno Lossin, linaro-mm-sig, Danilo Krummrich,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
On Wed, Jun 10, 2026 at 12:21:30PM -0400, Lyude Paul wrote:
> When writing up some rust code that used faux devices for unit testing, I
> noticed that we never actually added the Bound device context to
> faux::Registration's AsRef<device::Device> implementation. This being said:
> the Registration object itself is proof that a driver is bound to the
> device - so this should be safe.
>
> Signed-off-by: Lyude Paul <lyude@redhat.com>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 0/4] Rust bindings for gem shmem
2026-06-10 16:21 ` Lyude Paul
@ 2026-06-11 12:06 ` Danilo Krummrich
-1 siblings, 0 replies; 21+ messages in thread
From: Danilo Krummrich @ 2026-06-11 12:06 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Alice Ryhl, Simona Vetter, linux-kernel,
Sumit Semwal, linux-media, Rafael J . Wysocki, Thomas Zimmermann,
Maxime Ripard, David Airlie, Benno Lossin, linaro-mm-sig,
Mukesh Kumar Chaurasiya, Asahi Lina, Daniel Almeida,
Greg Kroah-Hartman
On Wed Jun 10, 2026 at 6:21 PM CEST, Lyude Paul wrote:
> Lyude Paul (4):
> rust: drm: gem: shmem: Add DmaResvGuard helper
> rust: drm: gem: shmem: Add vmap functions
> rust: faux: Allow retrieving a bound Device
> rust: drm: gem: Introduce shmem::Object::sg_table()
Acked-by: Danilo Krummrich <dakr@kernel.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v20 0/4] Rust bindings for gem shmem
@ 2026-06-11 12:06 ` Danilo Krummrich
0 siblings, 0 replies; 21+ messages in thread
From: Danilo Krummrich @ 2026-06-11 12:06 UTC (permalink / raw)
To: Lyude Paul
Cc: dri-devel, rust-for-linux, nouveau, Alexandre Courbot, Gary Guo,
Christian König, driver-core, Miguel Ojeda,
Maarten Lankhorst, Alice Ryhl, Simona Vetter, linux-kernel,
Sumit Semwal, linux-media, Rafael J . Wysocki, Maxime Ripard,
Benno Lossin, linaro-mm-sig, Mukesh Kumar Chaurasiya, Asahi Lina,
Daniel Almeida, Greg Kroah-Hartman
On Wed Jun 10, 2026 at 6:21 PM CEST, Lyude Paul wrote:
> Lyude Paul (4):
> rust: drm: gem: shmem: Add DmaResvGuard helper
> rust: drm: gem: shmem: Add vmap functions
> rust: faux: Allow retrieving a bound Device
> rust: drm: gem: Introduce shmem::Object::sg_table()
Acked-by: Danilo Krummrich <dakr@kernel.org>
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-06-11 12:06 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-10 16:21 [PATCH v20 0/4] Rust bindings for gem shmem Lyude Paul
2026-06-10 16:21 ` Lyude Paul
2026-06-10 16:21 ` [PATCH v20 1/4] rust: drm: gem: shmem: Add DmaResvGuard helper Lyude Paul
2026-06-10 16:21 ` Lyude Paul
2026-06-11 11:07 ` Alice Ryhl
2026-06-11 11:07 ` Alice Ryhl
2026-06-10 16:21 ` [PATCH v20 2/4] rust: drm: gem: shmem: Add vmap functions Lyude Paul
2026-06-10 16:21 ` Lyude Paul
2026-06-10 16:39 ` sashiko-bot
2026-06-11 11:10 ` Alice Ryhl
2026-06-11 11:10 ` Alice Ryhl
2026-06-10 16:21 ` [PATCH v20 3/4] rust: faux: Allow retrieving a bound Device Lyude Paul
2026-06-10 16:21 ` Lyude Paul
2026-06-10 16:37 ` sashiko-bot
2026-06-11 11:10 ` Alice Ryhl
2026-06-11 11:10 ` Alice Ryhl
2026-06-10 16:21 ` [PATCH v20 4/4] rust: drm: gem: Introduce shmem::Object::sg_table() Lyude Paul
2026-06-10 16:21 ` Lyude Paul
2026-06-10 16:46 ` sashiko-bot
2026-06-11 12:06 ` [PATCH v20 0/4] Rust bindings for gem shmem Danilo Krummrich
2026-06-11 12:06 ` Danilo Krummrich
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.