This is the next version of the shmem backed GEM objects series originally from Asahi, previously posted by Daniel Almeida. Along with bindings for shmem backed GEM objects.
This applies on top of Danilo and Abdiel's sgtable patches:
https://lkml.org/lkml/2025/8/28/1018
Asahi Lina (2): rust: helpers: Add bindings/wrappers for dma_resv_lock rust: drm: gem: shmem: Add DRM shmem helper abstraction
Lyude Paul (12): rust: drm: gem: Simplify use of generics rust: drm: gem: Add DriverFile type alias rust: drm: gem: Drop Object::SIZE rust: drm: gem: Support driver-private GEM object types rust: drm: gem: Add raw_dma_resv() function drm/gem/shmem: Extract drm_gem_shmem_init() from drm_gem_shmem_create() drm/gem/shmem: Extract drm_gem_shmem_release() from drm_gem_shmem_free() rust: gem: Introduce DriverObject::Args rust: drm: gem: Introduce SGTableRef rust: Add dma_buf stub bindings rust: drm: gem: Add export() callback rust: drm: gem: Add BaseObject::prime_export()
drivers/gpu/drm/drm_gem_shmem_helper.c | 98 +++++-- drivers/gpu/drm/nova/driver.rs | 8 +- drivers/gpu/drm/nova/gem.rs | 15 +- include/drm/drm_gem_shmem_helper.h | 2 + rust/bindings/bindings_helper.h | 3 + rust/helpers/dma-resv.c | 13 + rust/helpers/drm.c | 48 +++- rust/helpers/helpers.c | 1 + rust/kernel/dma_buf.rs | 39 +++ rust/kernel/drm/device.rs | 17 +- rust/kernel/drm/driver.rs | 5 +- rust/kernel/drm/gem/mod.rs | 274 +++++++++++++++---- rust/kernel/drm/gem/shmem.rs | 365 +++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 14 files changed, 783 insertions(+), 106 deletions(-) create mode 100644 rust/helpers/dma-resv.c create mode 100644 rust/kernel/dma_buf.rs create mode 100644 rust/kernel/drm/gem/shmem.rs
base-commit: 09f90256e8902793f594517ef440698585eb3595 prerequisite-patch-id: 0e1b1f9a665317ff569a37df6ff49cd1880b04f8 prerequisite-patch-id: 178b864e6d1b88ee299dcc05d1a7a4c89ec7ed51 prerequisite-patch-id: 7f72c4bfd0e5f50b6d2f8ce96751782894a3ba81 prerequisite-patch-id: 62fa6de7d3ae99dc54c092087bd716e6749545fd prerequisite-patch-id: 3d14d56ca93b0831837aa26b802100a250adeac6 prerequisite-patch-id: 7a12f4b0e7588874ce589b41b70671dc261b9468 prerequisite-patch-id: c44763ec35c4e4431e769df088b98424cbddf7df prerequisite-patch-id: a9e008c179b1c2fbe76654a191e5018880383d49 prerequisite-patch-id: 1e9ce500ce25188c575be608cd39e15a59836f83 prerequisite-patch-id: 39ca3a210a6c365434924c07a0c98a074eb73b97 prerequisite-patch-id: a747e05834cdb8b8f727e1f7c8b110c636cadab8 prerequisite-patch-id: 24833689bdecd3fc7a604e13bfe203ccd2fca6f0
From: Asahi Lina lina@asahilina.net
This is just for basic usage in the DRM shmem abstractions for implied locking, not intended as a full DMA Reservation abstraction yet.
Signed-off-by: Asahi Lina lina@asahilina.net Signed-off-by: Daniel Almeida daniel.almeida@collabora.com Signed-off-by: Lyude Paul lyude@redhat.com --- rust/bindings/bindings_helper.h | 1 + rust/helpers/dma-resv.c | 13 +++++++++++++ rust/helpers/helpers.c | 1 + 3 files changed, 15 insertions(+) create mode 100644 rust/helpers/dma-resv.c
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index c2cc52ee9945b..00fe0449ed6de 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -46,6 +46,7 @@ #include <linux/cpufreq.h> #include <linux/cpumask.h> #include <linux/cred.h> +#include <linux/dma-resv.h> #include <linux/device/faux.h> #include <linux/dma-direction.h> #include <linux/dma-mapping.h> diff --git a/rust/helpers/dma-resv.c b/rust/helpers/dma-resv.c new file mode 100644 index 0000000000000..05501cb814513 --- /dev/null +++ b/rust/helpers/dma-resv.c @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/dma-resv.h> + +int rust_helper_dma_resv_lock(struct dma_resv *obj, struct ww_acquire_ctx *ctx) +{ + return dma_resv_lock(obj, ctx); +} + +void rust_helper_dma_resv_unlock(struct dma_resv *obj) +{ + dma_resv_unlock(obj); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index e94542bf6ea74..77354382fbea0 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -20,6 +20,7 @@ #include "cred.c" #include "device.c" #include "dma.c" +#include "dma-resv.c" #include "drm.c" #include "err.c" #include "fs.c"
For retrieving a pointer to the struct dma_resv for a given GEM object. We also introduce it in a new trait, BaseObjectPrivate, which we automatically implement for all gem objects and don't expose to users outside of the crate.
Signed-off-by: Lyude Paul lyude@redhat.com --- rust/kernel/drm/gem/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index ec36cd9ea69ed..f901d4263ee87 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -186,6 +186,18 @@ fn create_mmap_offset(&self) -> Result<u64> {
impl<T: IntoGEMObject> BaseObject for T {}
+/// Crate-private base operations shared by all GEM object classes. +#[expect(unused)] +pub(crate) trait BaseObjectPrivate: IntoGEMObject { + /// Return a pointer to this object's dma_resv. + fn raw_dma_resv(&self) -> *mut bindings::dma_resv { + // SAFETY: `as_gem_obj()` always returns a valid pointer to the base DRM gem object + unsafe { (*self.as_raw()).resv } + } +} + +impl<T: IntoGEMObject> BaseObjectPrivate for T {} + /// A base GEM object. /// /// Invariants
In order to implement the gem export callback, we need a type to represent struct dma_buf. So - this commit introduces a set of stub bindings for dma_buf. These bindings provide a ref-counted DmaBuf object, but don't currently implement any functionality for using the DmaBuf.
Signed-off-by: Lyude Paul lyude@redhat.com
--- V3: * Rename as_ref() to from_raw()
Signed-off-by: Lyude Paul lyude@redhat.com --- rust/kernel/dma_buf.rs | 40 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 41 insertions(+) create mode 100644 rust/kernel/dma_buf.rs
diff --git a/rust/kernel/dma_buf.rs b/rust/kernel/dma_buf.rs new file mode 100644 index 0000000000000..a66829afcd129 --- /dev/null +++ b/rust/kernel/dma_buf.rs @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! DMA buffer API +//! +//! C header: [`include/linux/dma-buf.h`](srctree/include/linux/dma-buf.h) + +use bindings; +use kernel::types::*; + +/// A DMA buffer object. +/// +/// # Invariants +/// +/// The data layout of this type is equivalent to that of `struct dma_buf`. +#[repr(transparent)] +pub struct DmaBuf(Opaquebindings::dma_buf); + +// SAFETY: `struct dma_buf` is thread-safe +unsafe impl Send for DmaBuf {} +// SAFETY: `struct dma_buf` is thread-safe +unsafe impl Sync for DmaBuf {} + +#[expect(unused)] +impl DmaBuf { + /// Convert from a `*mut bindings::dma_buf` to a [`DmaBuf`]. + /// + /// # Safety + /// + /// The caller guarantees that `self_ptr` points to a valid initialized `struct dma_buf` for the + /// duration of the lifetime of `'a`, and promises to not violate rust's data aliasing rules + /// using the reference provided by this function. + pub(crate) unsafe fn from_raw<'a>(self_ptr: *mut bindings::dma_buf) -> &'a Self { + // SAFETY: Our data layout is equivalent to `dma_buf` . + unsafe { &*self_ptr.cast() } + } + + pub(crate) fn as_raw(&self) -> *mut bindings::dma_buf { + self.0.get() + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index fcffc3988a903..59242d83efe21 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -81,6 +81,7 @@ pub mod device_id; pub mod devres; pub mod dma; +pub mod dma_buf; pub mod driver; #[cfg(CONFIG_DRM = "y")] pub mod drm;
This introduces an optional export() callback for GEM objects, which is used to implement the drm_gem_object_funcs->export function.
Signed-off-by: Lyude Paul lyude@redhat.com --- drivers/gpu/drm/nova/gem.rs | 1 + rust/kernel/drm/gem/mod.rs | 72 +++++++++++++++++++++++++++++++++++- rust/kernel/drm/gem/shmem.rs | 6 ++- 3 files changed, 76 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs index 015cb56061a56..bbce6b0f4e6a4 100644 --- a/drivers/gpu/drm/nova/gem.rs +++ b/drivers/gpu/drm/nova/gem.rs @@ -16,6 +16,7 @@ #[pin_data] pub(crate) struct NovaObject {}
+#[vtable] impl gem::DriverObject for NovaObject { type Driver = NovaDriver; type Object = gem::Object<Self>; diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index f9f9727f14e4a..1ac25fc6d527b 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -8,7 +8,7 @@
use crate::{ alloc::flags::*, - bindings, + bindings, dma_buf, drm::driver::{AllocImpl, AllocOps}, drm::{self, private::Sealed}, error::{to_result, Result}, @@ -45,6 +45,7 @@ fn as_ref(&self) -> &kernel::drm::gem::OpaqueObject<D> { pub(crate) use impl_as_opaque;
/// GEM object functions, which must be implemented by drivers. +#[vtable] pub trait DriverObject: Sync + Send + Sized { /// Parent `Driver` for this object. type Driver: drm::Driver; @@ -69,6 +70,11 @@ fn open(_obj: &Self::Object, _file: &DriverFile<Self>) -> Result {
/// Close a handle to an existing object, associated with a File. fn close(_obj: &Self::Object, _file: &DriverFile<Self>) {} + + /// Optional handle for exporting a gem object. + fn export(_obj: &Self::Object, _flags: u32) -> Result<DmaBufSelf::Object> { + unimplemented!() + } }
/// Trait that represents a GEM object subtype @@ -138,6 +144,21 @@ extern "C" fn close_callback<T: DriverObject>( T::close(obj, file); }
+extern "C" fn export_callback<T: DriverObject>( + raw_obj: *mut bindings::drm_gem_object, + flags: i32, +) -> *mut bindings::dma_buf { + // SAFETY: `export_callback` is specified in the AllocOps structure for `Object<T>`, ensuring + // that `raw_obj` is contained within a `Object<T>`. + let obj = unsafe { T::Object::from_raw(raw_obj) }; + + match T::export(obj, flags as u32) { + // DRM takes a hold of the reference + Ok(buf) => buf.into_raw(), + Err(e) => e.to_ptr(), + } +} + impl<T: DriverObject> IntoGEMObject for Object<T> { fn as_raw(&self) -> *mut bindings::drm_gem_object { self.obj.get() @@ -248,7 +269,11 @@ impl<T: DriverObject> Object<T> { open: Some(open_callback::<T>), close: Some(close_callback::<T>), print_info: None, - export: None, + export: if T::HAS_EXPORT { + Some(export_callback::<T>) + } else { + None + }, pin: None, unpin: None, get_sg_table: None, @@ -375,6 +400,49 @@ fn as_raw(&self) -> *mut bindings::drm_gem_object {
impl<D: drm::Driver> Sealed for OpaqueObject<D> {}
+/// A [`dma_buf::DmaBuf`] which has been exported from a GEM object. +/// +/// The [`dma_buf::DmaBuf`] will be released when this type is dropped. +/// +/// # Invariants +/// +/// - `self.0` points to a valid initialized [`dma_buf::DmaBuf`] for the lifetime of this object. +/// - The GEM object from which this [`dma_buf::DmaBuf`] was exported from is guaranteed to be of +/// type `T`. +pub struct DmaBuf<T: IntoGEMObject>(NonNull<dma_buf::DmaBuf>, PhantomData<T>); + +impl<T: IntoGEMObject> Deref for DmaBuf<T> { + type Target = dma_buf::DmaBuf; + + #[inline] + fn deref(&self) -> &Self::Target { + // SAFETY: This pointer is guaranteed to be valid by our type invariants. + unsafe { self.0.as_ref() } + } +} + +impl<T: IntoGEMObject> Drop for DmaBuf<T> { + #[inline] + fn drop(&mut self) { + // SAFETY: + // - `dma_buf::DmaBuf` is guaranteed to have an identical layout to `struct dma_buf` + // by its type invariants. + // - We hold the last reference to this `DmaBuf`, making it safe to destroy. + unsafe { bindings::drm_gem_dmabuf_release(self.0.cast().as_ptr()) } + } +} + +impl<T: IntoGEMObject> DmaBuf<T> { + /// Leak the reference for this [`DmaBuf`] and return a raw pointer to it. + #[inline] + pub(crate) fn into_raw(self) -> *mut bindings::dma_buf { + let dma_ptr = self.as_raw(); + + core::mem::forget(self); + dma_ptr + } +} + pub(super) const fn create_fops() -> bindings::file_operations { // SAFETY: As by the type invariant, it is safe to initialize `bindings::file_operations` // zeroed. diff --git a/rust/kernel/drm/gem/shmem.rs b/rust/kernel/drm/gem/shmem.rs index 1437cda27a22c..b3a70e6001842 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -66,7 +66,11 @@ impl<T: DriverObject> Object<T> { open: Some(super::open_callback::<T>), close: Some(super::close_callback::<T>), print_info: Some(bindings::drm_gem_shmem_object_print_info), - export: None, + export: if T::HAS_EXPORT { + Some(super::export_callback::<T>) + } else { + None + }, pin: Some(bindings::drm_gem_shmem_object_pin), unpin: Some(bindings::drm_gem_shmem_object_unpin), get_sg_table: Some(bindings::drm_gem_shmem_object_get_sg_table),
We just added an export() callback that GEM objects can implement, but without any way of actually exporting a DmaBuf<T>. So let's add one by introducing bindings for drm_gem_prime_export().
Signed-off-by: Lyude Paul lyude@redhat.com --- rust/kernel/dma_buf.rs | 1 - rust/kernel/drm/gem/mod.rs | 24 +++++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/dma_buf.rs b/rust/kernel/dma_buf.rs index a66829afcd129..a2086918efd17 100644 --- a/rust/kernel/dma_buf.rs +++ b/rust/kernel/dma_buf.rs @@ -20,7 +20,6 @@ unsafe impl Send for DmaBuf {} // SAFETY: `struct dma_buf` is thread-safe unsafe impl Sync for DmaBuf {}
-#[expect(unused)] impl DmaBuf { /// Convert from a `*mut bindings::dma_buf` to a [`DmaBuf`]. /// diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 1ac25fc6d527b..75ffd5541e84c 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -11,7 +11,7 @@ bindings, dma_buf, drm::driver::{AllocImpl, AllocOps}, drm::{self, private::Sealed}, - error::{to_result, Result}, + error::{from_err_ptr, to_result, Result}, prelude::*, types::{ARef, AlwaysRefCounted, Opaque}, }; @@ -225,6 +225,28 @@ fn lookup_handle<D, F, O>(file: &drm::File<F>, handle: u32) -> Result<ARef<Self> Ok(unsafe { ARef::from_raw(obj.into()) }) }
+ /// Export a [`DmaBuf`] for this GEM object using the DRM prime helper library. + /// + /// `flags` should be a set of flags from [`fs::file::flags`](kernel::fs::file::flags). + fn prime_export(&self, flags: u32) -> Result<DmaBuf<Self>> { + // SAFETY: `as_raw()` always returns a valid pointer to an initialized `drm_gem_object`. + let dma_ptr = unsafe { bindings::drm_gem_prime_export(self.as_raw(), flags as i32) }; + + // `drm_gem_prime_export()` returns either an error pointer, or a valid pointer to an + // initialized `dma_buf` on success. + let dma_ptr = from_err_ptr(dma_ptr)?; + + // SAFETY: + // - We checked that dma_ptr is not an error, so it must point to an initialized dma_buf + // - We used drm_gem_prime_export(), so `dma_ptr` will remain valid until a call to + // `drm_gem_prime_release()` which we don't call here. + let dma_buf = unsafe { dma_buf::DmaBuf::from_raw(dma_ptr) }; + + // INVARIANT: We used drm_gem_prime_export() to create this dma_buf, fulfilling the + // invariant that this dma_buf came from a GEM object of type `Self`. + Ok(DmaBuf(dma_buf.into(), PhantomData)) + } + /// Creates an mmap offset to map the object from userspace. fn create_mmap_offset(&self) -> Result<u64> { // SAFETY: The arguments are valid per the type invariant.
linaro-mm-sig@lists.linaro.org