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, it also adds a few features that various users like Tyr and Asahi are interested in:
* The ability to pass custom arguments to new GEM objects (needed by Tyr) * OpaqueObject (to enable the use of custom private GEM objects, which I believe asahi wanted)
And replaces some of the hand-rolled API bindings (sg_table mainly) with some of the WIP patch series for adding kernel-wide bindings. It also addresses the comments from the code review of the last version of this patch series.
Currently doesn't apply on an upstream branch, but should very soon as all of the dependencies in this series are on a mailing list already.
The current branch this can be applied on top of is here: https://gitlab.freedesktop.org/lyudess/linux/-/commits/rust%2Fgem-shmem-base
Which is based on top of nova/nova-next with the following patch series applied: * My (hopefully final) gem bindings cleanup: https://lkml.org/lkml/2025/5/20/1541 * Benno's derive Zeroable series: https://lkml.org/lkml/2025/5/20/1446 * Abdiel's sg_table series: https://lwn.net/Articles/1020986/ Also, there is one FIXES patch on top of Abdiel's work to fix some iterator bugs. These fixes have already been mentioned on the mailing list and should not be needed for their V2 version
Asahi Lina (3): rust: helpers: Add bindings/wrappers for dma_resv_lock rust: drm: gem: shmem: Add DRM shmem helper abstraction rust: drm: gem: shmem: Add share_dma_resv to ObjectConfig
Lyude Paul (9): 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 BaseDriverObject::Args rust: drm: gem: Add OpaqueObject rust: drm: gem: Introduce OwnedSGTable 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/gem.rs | 6 +- include/drm/drm_gem_shmem_helper.h | 2 + rust/bindings/bindings_helper.h | 4 + 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/gem/mod.rs | 187 ++++++++++++- rust/kernel/drm/gem/shmem.rs | 370 +++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 11 files changed, 727 insertions(+), 42 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
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 31369b7b23884..409e9a595e051 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -18,6 +18,7 @@ #include <linux/blkdev.h> #include <linux/cpumask.h> #include <linux/cred.h> +#include <linux/dma-resv.h> #include <linux/device/faux.h> #include <linux/dma-mapping.h> #include <linux/dma-direction.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 20a4ee59acd89..3ba1652899c2b 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -15,6 +15,7 @@ #include "cpumask.c" #include "cred.c" #include "device.c" +#include "dma-resv.c" #include "drm.c" #include "err.c" #include "fs.c"
On 5/21/25 22:29, Lyude Paul wrote:
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.
Looks good in general, but my question is if it wouldn't be better to export the higher level drm_exec component instead?
The drm_exec component implements the necessary loop if you want to lock multiple GEM objects at the same time. As well as makes sure that those GEM objects can't be released while working with them.
Regtards, Christian.
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 31369b7b23884..409e9a595e051 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -18,6 +18,7 @@ #include <linux/blkdev.h> #include <linux/cpumask.h> #include <linux/cred.h> +#include <linux/dma-resv.h> #include <linux/device/faux.h> #include <linux/dma-mapping.h> #include <linux/dma-direction.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 20a4ee59acd89..3ba1652899c2b 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -15,6 +15,7 @@ #include "cpumask.c" #include "cred.c" #include "device.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 | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 992e098d0a3e2..1165417b22df6 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -186,6 +186,17 @@ fn create_mmap_offset(&self) -> Result<u64> {
impl<T: IntoGEMObject> BaseObject for T {}
+/// Crate-private base operations shared by all GEM object classes. +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 --- rust/kernel/dma_buf.rs | 39 +++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 40 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..318518ff0b28f --- /dev/null +++ b/rust/kernel/dma_buf.rs @@ -0,0 +1,39 @@ +// 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 {} + +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 as_ref<'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 cbacedfe54434..02d4311bb080c 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -49,6 +49,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 | 73 +++++++++++++++++++++++++++++++++++- rust/kernel/drm/gem/shmem.rs | 6 ++- 3 files changed, 77 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/nova/gem.rs b/drivers/gpu/drm/nova/gem.rs index f2f23320110dd..6cc7a65a50fae 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::BaseDriverObject 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 6d87e75690d2f..ef697f323e52c 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -10,7 +10,8 @@ alloc::flags::*, bindings, drm::{self, private::Sealed}, drm::driver::{AllocImpl, AllocOps}, - error::{to_result, Result}, + dma_buf, + error::{to_result, from_err_ptr, Result}, prelude::*, types::{ARef, AlwaysRefCounted, Opaque}, }; @@ -44,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 BaseDriverObject: Sync + Send + Sized { /// Parent `Driver` for this object. type Driver: drm::Driver; @@ -68,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 @@ -137,6 +144,21 @@ extern "C" fn close_callback<T: BaseDriverObject>( T::close(obj, file); }
+extern "C" fn export_callback<T: BaseDriverObject>( + 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::as_ref(raw_obj) }; + + match T::export(obj, flags as _) { + // DRM takes a hold of the reference + Ok(buf) => buf.into_raw(), + Err(e) => e.to_ptr(), + } +} + impl<T: BaseDriverObject> IntoGEMObject for Object<T> { fn as_raw(&self) -> *mut bindings::drm_gem_object { self.obj.get() @@ -247,7 +269,11 @@ impl<T: BaseDriverObject> 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, @@ -376,6 +402,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 bff038df93334..799f15c38cc36 100644 --- a/rust/kernel/drm/gem/shmem.rs +++ b/rust/kernel/drm/gem/shmem.rs @@ -77,7 +77,11 @@ impl<T: BaseDriverObject> 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/drm/gem/mod.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+)
diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index ef697f323e52c..ee74b9bdb7a1f 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -225,6 +225,29 @@ 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 a `drm_gem_object`. + // - `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(unsafe { + bindings::drm_gem_prime_export(self.as_raw(), flags as _) + })?; + + // 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::as_ref(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