Skip to contentSkip to conversation

Rust bindings for gem shmem + iosys_map

PATCH Superseded Lyude Paul 6 participants 18 messages Dec 2, 2025 – Feb 6

Subsystems (7)

v6 of 21 Superseded Latest is v21 →

lore
Share & tools
lore

Lyude Paul

author
#0 lore raw

v6 cover Rust bindings for gem shmem + iosys_map

This is the next version of the shmem backed GEM objects series originally from Asahi, previously posted by Daniel Almeida.

One of the major changes in this patch series is a much better interface around vmaps, which we achieve by introducing a new set of rust bindings for iosys_map.

The previous version of the patch series can be found here:

https://patchwork.freedesktop.org/series/156093/

Changelogs are per-patch

Asahi Lina (2):
rust: helpers: Add bindings/wrappers for dma_resv_lock
rust: drm: gem: shmem: Add DRM shmem helper abstraction

Lyude Paul (6):
rust/drm: Add gem::impl_aref_for_gem_obj!
rust: drm: gem: Add raw_dma_resv() function
rust: gem: Introduce DriverObject::Args
rust: drm: gem: Introduce shmem::SGTable
rust: Introduce iosys_map bindings
rust: drm/gem: Add vmap functions to shmem bindings

 drivers/gpu/drm/nova/gem.rs     |   5 +-
 drivers/gpu/drm/tyr/gem.rs      |   3 +-
 rust/bindings/bindings_helper.h |   3 +
 rust/helpers/dma-resv.c         |  13 +
 rust/helpers/drm.c              |  48 ++-
 rust/helpers/helpers.c          |   2 +
 rust/helpers/iosys_map.c        |  15 +
 rust/kernel/drm/gem/mod.rs      |  79 +++-
 rust/kernel/drm/gem/shmem.rs    | 433 ++++++++++++++++++++++
 rust/kernel/iosys_map.rs        | 614 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   1 +
 11 files changed, 1194 insertions(+), 22 deletions(-)
 create mode 100644 rust/helpers/dma-resv.c
 create mode 100644 rust/helpers/iosys_map.c
 create mode 100644 rust/kernel/drm/gem/shmem.rs
 create mode 100644 rust/kernel/iosys_map.rs

base-commit: 57dc2ea0b7bdb828c5d966d9135c28fe854933a4

Signature · 1 line
2.52.0
author
#1 lore raw

PATCH v6 1/8 rust/drm: Add gem::impl_aref_for_gem_obj!

In the future we're going to be introducing more GEM object types in rust then just gem::Object<T>. Since all types of GEM objects have refcounting, let's introduce a macro that we can use in the gem crate in order to copy this boilerplate implementation for each type: impl_aref_for_gem_obj!().

---
V5:
* Move .as_raw() call to let obj in dec_ref, to ensure that the reference to object is not live by the time that we call drm_gem_object_put().
* Add missing #[macro_export] annotation
V6:
* Add missing IntoGEMObject trait bound

 rust/kernel/drm/gem/mod.rs | 53 +++++++++++++++++++++++++++-----------
 1 file changed, 38 insertions(+), 15 deletions(-)
rust/kernel/drm/gem/mod.rs +38 -15
index a7f682e95c018..5c215e83c1b09 100644
@@ -15,6 +15,43 @@
 };
 use core::{ops::Deref, ptr::NonNull};
 
+/// A macro for implementing [`AlwaysRefCounted`] for any GEM object type.
+///
+/// Since all GEM objects use the same refcounting scheme.
+#[macro_export]
+macro_rules! impl_aref_for_gem_obj {
+    (
+        impl $( <$( $tparam_id:ident ),+> )? for $type:ty
+        $(
+            where
+                $( $bind_param:path : $bind_trait:path ),+
+        )?
+    ) => {
+        // SAFETY: All gem objects are refcounted
+        unsafe impl $( <$( $tparam_id ),+> )? $crate::types::AlwaysRefCounted for $type
+        where
+            Self: IntoGEMObject,
+            $( $( $bind_param : $bind_trait ),+ )?
+        {
+            fn inc_ref(&self) {
+                // SAFETY: The existence of a shared reference guarantees that the refcount is
+                // non-zero.
+                unsafe { bindings::drm_gem_object_get(self.as_raw()) };
+            }
+
+            unsafe fn dec_ref(obj: core::ptr::NonNull<Self>) {
+                // SAFETY: `obj` is a valid pointer to an `Object<T>`.
+                let obj = unsafe { obj.as_ref() }.as_raw();
+
+                // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+                unsafe { bindings::drm_gem_object_put(obj) };
+            }
+        }
+    };
+}
+
+pub(crate) use impl_aref_for_gem_obj;
+
 /// A type alias for retrieving a [`Driver`]s [`DriverFile`] implementation from its
 /// [`DriverObject`] implementation.
 ///
@@ -252,21 +289,7 @@ extern "C" fn free_callback(obj: *mut bindings::drm_gem_object) {
     }
 }
 
-// SAFETY: Instances of `Object<T>` are always reference-counted.
-unsafe impl<T: DriverObject> crate::types::AlwaysRefCounted for Object<T> {
-    fn inc_ref(&self) {
-        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
-        unsafe { bindings::drm_gem_object_get(self.as_raw()) };
-    }
-
-    unsafe fn dec_ref(obj: NonNull<Self>) {
-        // SAFETY: `obj` is a valid pointer to an `Object<T>`.
-        let obj = unsafe { obj.as_ref() };
-
-        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
-        unsafe { bindings::drm_gem_object_put(obj.as_raw()) }
-    }
-}
+impl_aref_for_gem_obj!(impl<T> for Object<T> where T: DriverObject);
 
 impl<T: DriverObject> super::private::Sealed for Object<T> {}
 
Signature · 1 line
2.52.0
author
#2 lore raw

PATCH v6 2/8 rust: helpers: Add bindings/wrappers for dma_resv_lock

In reply to Lyude Paul

From: Asahi Lina <[email protected]>

This is just for basic usage in the DRM shmem abstractions for implied locking, not intended as a full DMA Reservation abstraction yet.

---
 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
rust/bindings/bindings_helper.h +1 -0
index 2e43c66635a2c..07f79e125c329 100644
@@ -48,6 +48,7 @@
 #include <linux/cpumask.h>
 #include <linux/cred.h>
 #include <linux/debugfs.h>
+#include <linux/dma-resv.h>
 #include <linux/device/faux.h>
 #include <linux/dma-direction.h>
 #include <linux/dma-mapping.h>
rust/helpers/dma-resv.c new file +13 -0
new file mode 100644
index 0000000000000..05501cb814513
@@ -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);
+}
rust/helpers/helpers.c +1 -0
index 551da6c9b5064..36d40f911345c 100644
@@ -25,6 +25,7 @@
 #include "cred.c"
 #include "device.c"
 #include "dma.c"
+#include "dma-resv.c"
 #include "drm.c"
 #include "err.c"
 #include "irq.c"
Signature · 1 line
2.52.0
Reviewed-by on v6 2/8
#3 lore raw

Re: [PATCH v6 2/8] rust: helpers: Add bindings/wrappers for dma_resv_lock

Quoted 21 lines · Lyude Paul

From: Asahi Lina <[email protected]>

This is just for basic usage in the DRM shmem abstractions for implied locking, not intended as a full DMA Reservation abstraction yet.

---
 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
rust/bindings/bindings_helper.h +0 -0
index 2e43c66635a2c..07f79e125c329 100644

rust/bindings/bindings_helper.h line 54

Quoted 7 lines
rust/bindings/bindings_helper.h +1 -0
@@ -48,6 +48,7 @@
 #include <linux/cpumask.h>
 #include <linux/cred.h>
 #include <linux/debugfs.h>
+#include <linux/dma-resv.h>
 #include <linux/device/faux.h>
 #include <linux/dma-direction.h>
 #include <linux/dma-mapping.h>

nit: alphabetical order

Quoted 24 lines
rust/helpers/dma-resv.c new file +13 -0
new file mode 100644
index 0000000000000..05501cb814513
@@ -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);
+}
rust/helpers/helpers.c +0 -0
index 551da6c9b5064..36d40f911345c 100644

rust/helpers/helpers.c line 31

Quoted 7 lines
rust/helpers/helpers.c +1 -0
@@ -25,6 +25,7 @@
 #include "cred.c"
 #include "device.c"
 #include "dma.c"
+#include "dma-resv.c"
 #include "drm.c"
 #include "err.c"
 #include "irq.c"

with that fixed

Janne

author
#4 lore raw

PATCH v6 3/8 rust: drm: gem: Add raw_dma_resv() function

In reply to Lyude Paul

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.

---
 rust/kernel/drm/gem/mod.rs | 12 ++++++++++++
 1 file changed, 12 insertions(+)
rust/kernel/drm/gem/mod.rs +12 -0
index 5c215e83c1b09..ec3c1b1775196 100644
@@ -199,6 +199,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
Signature · 1 line
2.52.0
author
#5 lore raw

PATCH v6 4/8 rust: gem: Introduce DriverObject::Args

In reply to Lyude Paul

This is an associated type that may be used in order to specify a data-type to pass to gem objects when construction them, allowing for drivers to more easily initialize their private-data for gem objects.

---
V3:
* s/BaseDriverObject/DriverObject/
V4:
* Fix leftover reference to BaseObjectDriver in rustdoc for DriverObject::Args
V6:
* Fix build errors in Tyr

 drivers/gpu/drm/nova/gem.rs |  5 +++--
 drivers/gpu/drm/tyr/gem.rs  |  3 ++-
 rust/kernel/drm/gem/mod.rs  | 13 ++++++++++---
 3 files changed, 15 insertions(+), 6 deletions(-)
drivers/gpu/drm/nova/gem.rs +3 -2
index 2760ba4f3450b..173077eeb2def 100644
@@ -18,8 +18,9 @@ pub(crate) struct NovaObject {}
 
 impl gem::DriverObject for NovaObject {
     type Driver = NovaDriver;
+    type Args = ();
 
-    fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit<Self, Error> {
+    fn new(_dev: &NovaDevice, _size: usize, _args: Self::Args) -> impl PinInit<Self, Error> {
         try_pin_init!(NovaObject {})
     }
 }
@@ -33,7 +34,7 @@ pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result<ARef<gem::Object<Self
             return Err(EINVAL);
         }
 
-        gem::Object::new(dev, aligned_size)
+        gem::Object::new(dev, aligned_size, ())
     }
 
     /// Look up a GEM object handle for a `File` and return an `ObjectRef` for it.
drivers/gpu/drm/tyr/gem.rs +2 -1
index 1273bf89dbd5d..bb5e7871efa94 100644
@@ -11,8 +11,9 @@ pub(crate) struct TyrObject {}
 
 impl gem::DriverObject for TyrObject {
     type Driver = TyrDriver;
+    type Args = ();
 
-    fn new(_dev: &TyrDevice, _size: usize) -> impl PinInit<Self, Error> {
+    fn new(_dev: &TyrDevice, _size: usize, _args: ()) -> impl PinInit<Self, Error> {
         try_pin_init!(TyrObject {})
     }
 }
rust/kernel/drm/gem/mod.rs +10 -3
index ec3c1b1775196..4bcaee2d4b308 100644
@@ -64,8 +64,15 @@ pub trait DriverObject: Sync + Send + Sized {
     /// Parent `Driver` for this object.
     type Driver: drm::Driver;
 
+    /// The data type to use for passing arguments to [`DriverObject::new`].
+    type Args;
+
     /// Create a new driver data object for a GEM object of a given size.
-    fn new(dev: &drm::Device<Self::Driver>, size: usize) -> impl PinInit<Self, Error>;
+    fn new(
+        dev: &drm::Device<Self::Driver>,
+        size: usize,
+        args: Self::Args,
+    ) -> impl PinInit<Self, Error>;
 
     /// Open a new handle to an existing object, associated with a File.
     fn open(_obj: &<Self::Driver as drm::Driver>::Object, _file: &DriverFile<Self>) -> Result {
@@ -244,11 +251,11 @@ impl<T: DriverObject> Object<T> {
     };
 
     /// Create a new GEM object.
-    pub fn new(dev: &drm::Device<T::Driver>, size: usize) -> Result<ARef<Self>> {
+    pub fn new(dev: &drm::Device<T::Driver>, size: usize, args: T::Args) -> Result<ARef<Self>> {
         let obj: Pin<KBox<Self>> = KBox::pin_init(
             try_pin_init!(Self {
                 obj: Opaque::new(bindings::drm_gem_object::default()),
-                data <- T::new(dev, size),
+                data <- T::new(dev, size, args),
             }),
             GFP_KERNEL,
         )?;
Signature · 1 line
2.52.0
Reviewed-by on v6 4/8
#6 lore raw

Re: [PATCH v6 4/8] rust: gem: Introduce DriverObject::Args

Quoted 51 lines · Lyude Paul

This is an associated type that may be used in order to specify a data-type to pass to gem objects when construction them, allowing for drivers to more easily initialize their private-data for gem objects.

---
V3:
* s/BaseDriverObject/DriverObject/
V4:
* Fix leftover reference to BaseObjectDriver in rustdoc for DriverObject::Args
V6:
* Fix build errors in Tyr

 drivers/gpu/drm/nova/gem.rs |  5 +++--
 drivers/gpu/drm/tyr/gem.rs  |  3 ++-
 rust/kernel/drm/gem/mod.rs  | 13 ++++++++++---
 3 files changed, 15 insertions(+), 6 deletions(-)
drivers/gpu/drm/nova/gem.rs +3 -2
index 2760ba4f3450b..173077eeb2def 100644
@@ -18,8 +18,9 @@ pub(crate) struct NovaObject {}
 
 impl gem::DriverObject for NovaObject {
     type Driver = NovaDriver;
+    type Args = ();
 
-    fn new(_dev: &NovaDevice, _size: usize) -> impl PinInit<Self, Error> {
+    fn new(_dev: &NovaDevice, _size: usize, _args: Self::Args) -> impl PinInit<Self, Error> {
         try_pin_init!(NovaObject {})
     }
 }
@@ -33,7 +34,7 @@ pub(crate) fn new(dev: &NovaDevice, size: usize) -> Result<ARef<gem::Object<Self
             return Err(EINVAL);
         }
 
-        gem::Object::new(dev, aligned_size)
+        gem::Object::new(dev, aligned_size, ())
     }
 
     /// Look up a GEM object handle for a `File` and return an `ObjectRef` for it.
drivers/gpu/drm/tyr/gem.rs +0 -0
index 1273bf89dbd5d..bb5e7871efa94 100644

drivers/gpu/drm/tyr/gem.rs pub(crate) struct TyrObject {} · line 16

Quoted 7 lines
drivers/gpu/drm/tyr/gem.rs +2 -1
@@ -11,8 +11,9 @@ pub(crate) struct TyrObject {}
 
 impl gem::DriverObject for TyrObject {
     type Driver = TyrDriver;
+    type Args = ();
 
-    fn new(_dev: &TyrDevice, _size: usize) -> impl PinInit<Self, Error> {
+    fn new(_dev: &TyrDevice, _size: usize, _args: ()) -> impl PinInit<Self, Error> {

nit: should the type for _args be Self::Args for consistency?

Quoted 40 lines
drivers/gpu/drm/tyr/gem.rs +0 -0
         try_pin_init!(TyrObject {})
     }
 }
rust/kernel/drm/gem/mod.rs +10 -3
index ec3c1b1775196..4bcaee2d4b308 100644
@@ -64,8 +64,15 @@ pub trait DriverObject: Sync + Send + Sized {
     /// Parent `Driver` for this object.
     type Driver: drm::Driver;
 
+    /// The data type to use for passing arguments to [`DriverObject::new`].
+    type Args;
+
     /// Create a new driver data object for a GEM object of a given size.
-    fn new(dev: &drm::Device<Self::Driver>, size: usize) -> impl PinInit<Self, Error>;
+    fn new(
+        dev: &drm::Device<Self::Driver>,
+        size: usize,
+        args: Self::Args,
+    ) -> impl PinInit<Self, Error>;
 
     /// Open a new handle to an existing object, associated with a File.
     fn open(_obj: &<Self::Driver as drm::Driver>::Object, _file: &DriverFile<Self>) -> Result {
@@ -244,11 +251,11 @@ impl<T: DriverObject> Object<T> {
     };
 
     /// Create a new GEM object.
-    pub fn new(dev: &drm::Device<T::Driver>, size: usize) -> Result<ARef<Self>> {
+    pub fn new(dev: &drm::Device<T::Driver>, size: usize, args: T::Args) -> Result<ARef<Self>> {
         let obj: Pin<KBox<Self>> = KBox::pin_init(
             try_pin_init!(Self {
                 obj: Opaque::new(bindings::drm_gem_object::default()),
-                data <- T::new(dev, size),
+                data <- T::new(dev, size, args),
             }),
             GFP_KERNEL,
         )?;
Signature · 1 line
2.52.0

Janne

author
#7 lore raw

PATCH v6 5/8 rust: drm: gem: shmem: Add DRM shmem helper abstraction

In reply to Lyude Paul

From: Asahi Lina <[email protected]>

The DRM shmem helper includes common code useful for drivers which allocate GEM objects as anonymous shmem. Add a Rust abstraction for this. Drivers can choose the raw GEM implementation or the shmem layer, depending on their needs.

---

V2:
* Use the drm_gem_shmem_init() and drm_gem_shmem_release() that I extracted so we can handle memory allocation in rust, which means we no longer have to handle freeing rust members of the struct by hand and have a closer implementation to the main gem object
(this also gets rid of gem_create_object)
* Get rid of GemObjectRef and UniqueGemObjectRef, we have ARef<T> at home.
* Use Device<T::Driver> in Object<T>
* Cleanup Object::<T>::new() a bit:
* Cleanup safety comment
* Use cast_mut()
* Just import container_of!(), we use it all over anyhow
* mut_shmem() -> as_shmem(), make it safe (there's no reason for being unsafe)
* Remove any *const and *muts in structs, just use NonNull
* Get rid of the previously hand-rolled sg_table bindings in shmem, use the bindings from Abdiel's sg_table patch series
* Add a TODO at the top about DMA reservation APIs and a desire for WwMutex
* Get rid of map_wc() and replace it with a new ObjectConfig struct. While it currently only specifies the map_wc flag, the idea here is that settings like map_wc() and parent_resv_obj() shouldn't be exposed as normal functions since the only place where it's safe to set them is when we're still guaranteed unique access to the GEM object, e.g. before returning it to the caller. Using a struct instead of individual arguments here is mainly because we'll be adding at least one more argument, and there's enough other gem shmem settings that trying to add all of them as individual function arguments in the future would be a bit messy.
* Get rid of vm_numa_fields!, Lina didn't like this macro much either and I think that it's fine for us to just specify the #[cfg(…)] attributes by hand since we only need to do it twice.
* Set drm_gem_object_funcs.vm_ops directly to drm_gem_shmem_vm_ops, don't export the various shmem funcs. I'm not sure why this wasn't possible before but it seems to work fine now.
V4:
* Switch from OpaqueObject to a normal Object<T> now that we've removed it
* Remove dev from Object, it's redundant as drm_gem_object already has a device pointer we can use.
* Use &raw instead of addr_of!()
V5:
* Fix typo in shmem::Object::as_raw()
* Add type invariant around obj always being a valid drm_gem_shmem_object for the duration of the lifetime of Object.
* Use Opaque::cast_from() instead of unrestricted casts

 rust/bindings/bindings_helper.h |   2 +
 rust/helpers/drm.c              |  48 ++++++-
 rust/kernel/drm/gem/mod.rs      |   3 +-
 rust/kernel/drm/gem/shmem.rs    | 225 ++++++++++++++++++++++++++++++++
 4 files changed, 276 insertions(+), 2 deletions(-)
 create mode 100644 rust/kernel/drm/gem/shmem.rs
rust/bindings/bindings_helper.h +2 -0
index 07f79e125c329..ad644f3d62cc3 100644
@@ -33,6 +33,7 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_file.h>
 #include <drm/drm_gem.h>
+#include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_ioctl.h>
 #include <kunit/test.h>
 #include <linux/auxiliary_bus.h>
@@ -60,6 +61,7 @@
 #include <linux/interrupt.h>
 #include <linux/fs.h>
 #include <linux/ioport.h>
+#include <linux/iosys-map.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
 #include <linux/mdio.h>
rust/helpers/drm.c +47 -1

57 lines not on this page · raw message

rust/kernel/drm/gem/mod.rs +2 -1
index 4bcaee2d4b308..c5af551f7e13e 100644
@@ -3,6 +3,8 @@
 //! DRM GEM API
 //!
 //! C header: [`include/drm/drm_gem.h`](srctree/include/drm/drm_gem.h)
+#[cfg(CONFIG_DRM_GEM_SHMEM_HELPER = "y")]
+pub mod shmem;
 
 use crate::{
     alloc::flags::*,
@@ -207,7 +209,6 @@ 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 {
rust/kernel/drm/gem/shmem.rs new file +225 -0

225 lines not on this page · raw message

Signature · 1 line
2.52.0
#8 lore raw

Re: [PATCH v6 5/8] rust: drm: gem: shmem: Add DRM shmem helper abstraction

Hello Lyude,

rust/kernel/drm/gem/shmem.rs line 58

Quoted 11 lines · On Tue, 2 Dec 2025 17:03:31 -0500 Lyude Paul <[email protected]> wrote:
rust/kernel/drm/gem/shmem.rs new file +11 -0
+#[pin_data]
+pub struct Object<T: DriverObject> {
+    #[pin]
+    obj: Opaque<bindings::drm_gem_shmem_object>,
+    // Parent object that owns this object's DMA reservation object
+    parent_resv_obj: Option<ARef<Object<T>>>,
+    #[pin]
+    inner: T,
+}
+
+super::impl_aref_for_gem_obj!(impl<T> for Object<T> where T: DriverObject);

As part of our integration in Tyr, we noticed we'll need Object<T> to be Sync+Send. Given T already has to be Sync+Send (part of the DriverObject trait constraints), it shouldn't be a problem to have Object<T> implement Sync+Send too, since, AFAICT, all the functions in the Object<T> are thread-safe (resv lock held when acting on the underlying BO, either explicitly, or implicitly through the C API).

Regards,

Boris

author
#9 lore raw

PATCH v6 6/8 rust: drm: gem: Introduce shmem::SGTable

In reply to Lyude Paul

Currently we expose the ability to retrieve an SGTable for an shmem gem object using gem::shmem::Object::<T>::sg_table(). However, this only gives us a borrowed reference. This being said - retrieving an SGTable is a fallible operation, and as such it's reasonable that a driver may want to hold onto an SGTable for longer then a reference would allow in order to avoid having to deal with fallibility every time they want to access the SGTable. One such driver with this usecase is the Asahi driver.

So to support this, let's introduce shmem::SGTable - which both holds a pointer to the SGTable and a reference to its respective GEM object in order to keep the GEM object alive for as long as the shmem::SGTable. The type can be used identically to a normal SGTable.

---
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

 rust/kernel/drm/gem/shmem.rs | 50 ++++++++++++++++++++++++++++++++++++
 1 file changed, 50 insertions(+)
rust/kernel/drm/gem/shmem.rs +50 -0
index 45b95d60a3ec7..21ccb6c1824be 100644
@@ -173,6 +173,25 @@ pub fn sg_table(&self) -> Result<&scatterlist::SGTable> {
         // pointer to a scatterlist
         Ok(unsafe { scatterlist::SGTable::from_raw(sgt) })
     }
+
+    /// Creates (if necessary) and returns an owned reference to a scatter-gather table of DMA pages
+    /// for this object.
+    ///
+    /// This is the same as [`sg_table`](Self::sg_table), except that it instead returns an
+    /// [`shmem::SGTable`] which holds a reference to the associated gem object, instead of a
+    /// reference to an [`scatterlist::SGTable`].
+    ///
+    /// This will pin the object in memory.
+    ///
+    /// [`shmem::SGTable`]: SGTable
+    pub fn owned_sg_table(&self) -> Result<SGTable<T>> {
+        Ok(SGTable {
+            sgt: self.sg_table()?.into(),
+            // INVARIANT: We take an owned refcount to `self` here, ensuring that `sgt` remains
+            // valid for as long as this `SGTable`.
+            _owner: self.into(),
+        })
+    }
 }
 
 impl<T: DriverObject> Deref for Object<T> {
@@ -223,3 +242,34 @@ impl<T: DriverObject> driver::AllocImpl for Object<T> {
         dumb_map_offset: None,
     };
 }
+
+/// An owned reference to a scatter-gather table of DMA address spans for a GEM shmem object.
+///
+/// This object holds an owned reference to the underlying GEM shmem object, ensuring that the
+/// [`scatterlist::SGTable`] referenced by this type remains valid for the lifetime of this object.
+///
+/// # Invariants
+///
+/// - `sgt` is kept alive by `_owner`, ensuring it remains valid for as long as `Self`.
+/// - `sgt` corresponds to the owned object in `_owner`.
+/// - This object is only exposed in situations where we know the underlying `SGTable` will not be
+///   modified for the lifetime of this object. Thus, it is safe to send/access this type across
+///   threads.
+pub struct SGTable<T: DriverObject> {
+    sgt: NonNull<scatterlist::SGTable>,
+    _owner: ARef<Object<T>>,
+}
+
+// SAFETY: This object is thread-safe via our type invariants.
+unsafe impl<T: DriverObject> Send for SGTable<T> {}
+// SAFETY: This object is thread-safe via our type invariants.
+unsafe impl<T: DriverObject> Sync for SGTable<T> {}
+
+impl<T: DriverObject> Deref for SGTable<T> {
+    type Target = scatterlist::SGTable;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: Creating an immutable reference to this is safe via our type invariants.
+        unsafe { self.sgt.as_ref() }
+    }
+}
Signature · 1 line
2.52.0
author
#10 lore raw

PATCH v6 7/8 rust: Introduce iosys_map bindings

In reply to Lyude Paul

This introduces a set of bindings for working with iosys_map in rust code. The design of this is heavily based off the design for both the io and dma_map bindings for Rust.

---
V5:
- Fix incorrect field size being passed to iosys_map_memcpy_to()
- Add an additional unit test, basic_macro(), which can successfully catch the above issue so it doesn't happen again in the future.
V6:
- Drop as_slice/as_mut_slice (Alice Rhyl)

 rust/helpers/helpers.c   |   1 +
 rust/helpers/iosys_map.c |  15 +
 rust/kernel/iosys_map.rs | 614 +++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs       |   1 +
 4 files changed, 631 insertions(+)
 create mode 100644 rust/helpers/iosys_map.c
 create mode 100644 rust/kernel/iosys_map.rs
rust/helpers/helpers.c +1 -0
index 36d40f911345c..d549af697bd60 100644
@@ -31,6 +31,7 @@
 #include "irq.c"
 #include "fs.c"
 #include "io.c"
+#include "iosys_map.c"
 #include "jump_label.c"
 #include "kunit.c"
 #include "maple_tree.c"
rust/helpers/iosys_map.c new file +15 -0
new file mode 100644
index 0000000000000..b105261c3cf8a
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/iosys-map.h>
+
+void rust_helper_iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
+				     const void *src, size_t len)
+{
+	iosys_map_memcpy_to(dst, dst_offset, src, len);
+}
+
+void rust_helper_iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
+				       size_t src_offset, size_t len)
+{
+	iosys_map_memcpy_from(dst, src, src_offset, len);
+}
rust/kernel/iosys_map.rs new file +614 -0

614 lines not on this page · raw message

rust/kernel/lib.rs +1 -0
index 2581a356d1141..f727dbe2c8254 100644
@@ -101,6 +101,7 @@
 pub mod init;
 pub mod io;
 pub mod ioctl;
+pub mod iosys_map;
 pub mod iov;
 pub mod irq;
 pub mod jump_label;
Signature · 1 line
2.52.0
#11 lore raw

Re: [PATCH v6 7/8] rust: Introduce iosys_map bindings

Quoted 257 lines · Lyude Paul

This introduces a set of bindings for working with iosys_map in rust code. The design of this is heavily based off the design for both the io and dma_map bindings for Rust.

---
V5:
- Fix incorrect field size being passed to iosys_map_memcpy_to()
- Add an additional unit test, basic_macro(), which can successfully catch the above issue so it doesn't happen again in the future.
V6:
- Drop as_slice/as_mut_slice (Alice Rhyl)

 rust/helpers/helpers.c   |   1 +
 rust/helpers/iosys_map.c |  15 +
 rust/kernel/iosys_map.rs | 614 +++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs       |   1 +
 4 files changed, 631 insertions(+)
 create mode 100644 rust/helpers/iosys_map.c
 create mode 100644 rust/kernel/iosys_map.rs
rust/helpers/helpers.c +1 -0
index 36d40f911345c..d549af697bd60 100644
@@ -31,6 +31,7 @@
 #include "irq.c"
 #include "fs.c"
 #include "io.c"
+#include "iosys_map.c"
 #include "jump_label.c"
 #include "kunit.c"
 #include "maple_tree.c"
rust/helpers/iosys_map.c new file +15 -0
new file mode 100644
index 0000000000000..b105261c3cf8a
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/iosys-map.h>
+
+void rust_helper_iosys_map_memcpy_to(struct iosys_map *dst, size_t dst_offset,
+				     const void *src, size_t len)
+{
+	iosys_map_memcpy_to(dst, dst_offset, src, len);
+}
+
+void rust_helper_iosys_map_memcpy_from(void *dst, const struct iosys_map *src,
+				       size_t src_offset, size_t len)
+{
+	iosys_map_memcpy_from(dst, src, src_offset, len);
+}
rust/kernel/iosys_map.rs new file +196 -0
new file mode 100644
index 0000000000000..884a3d2be3348
@@ -0,0 +1,614 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! IO-agnostic memory mapping interfaces.
+//!
+//! This crate provides bindings for the `struct iosys_map` type, which provides a common interface
+//! for memory mappings which can reside within coherent memory, or within IO memory.
+//!
+//! C header: [`include/linux/iosys-map.h`](srctree/include/linux/pci.h)
+
+use crate::{
+    prelude::*,
+    transmute::{AsBytes, FromBytes},
+};
+use bindings;
+use core::{
+    marker::PhantomData,
+    mem::{self, MaybeUninit},
+    ops::{Deref, DerefMut, Range},
+};
+
+/// Raw unsized representation of a `struct iosys_map`.
+///
+/// This struct is a transparent wrapper around `struct iosys_map`. The C API does not provide the
+/// size of the mapping by default, and thus this type also does not include the size of the
+/// mapping. As such, it cannot be used for actually accessing the underlying data pointed to by the
+/// mapping.
+///
+/// With the exception of kernel crates which may provide their own wrappers around `RawIoSysMap`,
+/// users will typically not interact with this type directly.
+pub struct RawIoSysMap<T: AsBytes + FromBytes>(bindings::iosys_map, PhantomData<T>);
+
+impl<T: AsBytes + FromBytes> RawIoSysMap<T> {
+    /// Convert from a raw `bindings::iosys_map`.
+    #[expect(unused)]
+    #[inline]
+    pub(crate) fn from_raw(val: bindings::iosys_map) -> Self {
+        Self(val, PhantomData)
+    }
+
+    /// Convert from a `RawIoSysMap<T>` to a raw `bindings::iosys_map` ref.
+    #[inline]
+    pub(crate) fn as_raw(&self) -> &bindings::iosys_map {
+        &self.0
+    }
+
+    /// Convert from a `RawIoSysMap<T>` to a raw mutable `bindings::iosys_map` ref.
+    #[inline]
+    pub(crate) fn as_raw_mut(&mut self) -> &mut bindings::iosys_map {
+        &mut self.0
+    }
+
+    /// Returns whether the mapping is within IO memory space or not.
+    #[inline]
+    pub fn is_iomem(&self) -> bool {
+        self.0.is_iomem
+    }
+
+    /// Returns the size of a single item in this mapping.
+    pub const fn item_size(&self) -> usize {
+        mem::size_of::<T>()
+    }
+
+    /// Returns a mutable address to the memory pointed to by this iosys map.
+    ///
+    /// Note that this address is not guaranteed to reside in system memory, and may reside in IO
+    /// memory.
+    #[inline]
+    pub fn as_mut_ptr(&self) -> *mut T {
+        if self.is_iomem() {
+            // SAFETY: We confirmed above that this iosys map is contained within iomem, so it's
+            // safe to read vaddr_iomem
+            unsafe { self.0.__bindgen_anon_1.vaddr_iomem }
+        } else {
+            // SAFETY: We confirmed above that this iosys map is not contaned within iomem, so it's
+            // safe to read vaddr.
+            unsafe { self.0.__bindgen_anon_1.vaddr }
+        }
+        .cast()
+    }
+
+    /// Returns an immutable address to the memory pointed to by this iosys map.
+    ///
+    /// Note that this address is not guaranteed to reside in system memory, and may reside in IO
+    /// memory.
+    #[inline]
+    pub fn as_ptr(&self) -> *const T {
+        self.as_mut_ptr().cast_const()
+    }
+}
+
+// SAFETY: As we make no guarantees about the validity of the mapping, there's no issue with sending
+// this type between threads.
+unsafe impl<T: AsBytes + FromBytes> Send for RawIoSysMap<T> {}
+
+impl<T: AsBytes + FromBytes> Clone for RawIoSysMap<T> {
+    fn clone(&self) -> Self {
+        Self(self.0, PhantomData)
+    }
+}
+
+/// A sized version of a [`RawIoSysMap`].
+///
+/// Since this type includes the size of the [`RawIoSysMap`], it can be used for accessing the
+/// underlying data pointed to by it.
+///
+/// # Invariants
+///
+/// - The iosys mapping referenced by this type is guaranteed to be of at least `size` bytes in
+///   size
+/// - The iosys mapping referenced by this type is valid for the lifetime `'a`.
+#[derive(Clone)]
+pub struct IoSysMapRef<'a, T: AsBytes + FromBytes> {
+    map: RawIoSysMap<T>,
+    size: usize,
+    _p: PhantomData<&'a T>,
+}
+
+impl<'a, T: AsBytes + FromBytes> IoSysMapRef<'a, T> {
+    /// Create a new [`IoSysMapRef`] from a [`RawIoSysMap`].
+    ///
+    /// # Safety
+    ///
+    /// - The caller guarantees that the mapping referenced by `map` is of at least `size` bytes in
+    ///   size.
+    /// - The caller guarantees that the mapping referenced by `map` remains valid for the lifetime
+    ///   of `'a`.
+    #[allow(unused)]
+    pub(crate) unsafe fn new(map: RawIoSysMap<T>, size: usize) -> IoSysMapRef<'a, T> {
+        // INVARIANT: Our safety contract fulfills the type invariants of `IoSysMapRef`.
+        IoSysMapRef {
+            map,
+            size,
+            _p: PhantomData,
+        }
+    }
+
+    /// Return the size of the `IoSysMapRef`.
+    #[inline]
+    pub fn size(&self) -> usize {
+        self.size
+    }
+
+    /// Writes `src` to the region starting from `offset`.
+    ///
+    /// `offset` is in units of `T`, not the number of bytes.
+    ///
+    /// This function can return the following errors:
+    ///
+    /// * [`EOVERFLOW`] if calculating the length of the slice results in an overflow.
+    /// * [`EINVAL`] if the slice would go out of bounds of the memory region.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use kernel::iosys_map::*;
+    ///
+    /// # fn test() -> Result {
+    /// # let mut map = tests::VecIoSysMap::new(&[0; 3])?;
+    /// # {
+    /// # let mut map = map.get();
+    /// map.write(&[1, 2, 3], 0)?; // (now [1, 2, 3])
+    /// map.write(&[4], 2)?; // (now [1, 2, 4])
+    /// # }
+    /// #
+    /// # map.assert_eq(&[1, 2, 4]);
+    /// #
+    /// # Ok::<(), Error>(()) }
+    /// # assert!(test().is_ok());
+    /// ```
+    pub fn write(&mut self, src: &[T], offset: usize) -> Result {
+        let range = self.compute_range(offset, src.len())?;
+
+        // SAFETY:
+        // - The address pointed to by this iosys_map is guaranteed to be valid via IoSysMapRef's
+        //   type invariants.
+        // - We checked that this range of memory is within bounds above
+        unsafe {
+            bindings::iosys_map_memcpy_to(
+                self.as_raw_mut(),
+                range.start,
+                src.as_ptr().cast(),
+                range.len(),
+            )
+        };
+
+        Ok(())
+    }
+
+    /// Attempt to compute the offset of an item within the iosys map using its index.
+    ///
+    /// Returns an error if an overflow occurs.
+    ///
+    /// # Safety
+    ///
+    /// This function checks for overflows, but it explicitly does not check if the offset goes out
+    /// of bounds. It is the caller's responsibility to check for this before using the returned

rust/kernel/iosys_map.rs line 206

Quoted 10 lines
rust/kernel/iosys_map.rs new file +10 -0
+    /// offset with the iosys_map API.
+    unsafe fn item_from_index(&self, idx: usize) -> Result<usize> {
+        self.item_size().checked_mul(idx).ok_or(EOVERFLOW)
+    }
+
+    /// Compute the range within this mapping a specific data type at a given offset would occupy.
+    ///
+    /// This function returns the computed range if it doesn't overflow, but does not check whether
+    /// or not the range is within the bounds of the allocated region pointed to by this iosys
+    /// mapping.

The part of the comment starting with "but does not check ..." doesn't appear to be accurate anymore. It checks range_end > self.size. The guarantees on successful return below contradict it as well.

rust/kernel/iosys_map.rs line 210

Quoted 4 lines
rust/kernel/iosys_map.rs new file +4 -0
+    ///
+    /// On success, the range returned by this function is guaranteed:
+    ///
+    /// * To be a valid range of memory within the virtual mapping for this gem object.

This isn't a gem object anymore

rust/kernel/iosys_map.rs line 223

Quoted 13 lines
rust/kernel/iosys_map.rs new file +13 -0
+    /// * To be properly aligned to [`RawIoSysMap::item_size()`].
+    fn compute_range(&self, offset: usize, count: usize) -> Result<Range<usize>> {
+        // SAFETY: If the offset is out of bounds, we'll catch this via overflow checks or when
+        // checking range_end.
+        let offset = unsafe { self.item_from_index(offset)? };
+        let range_size = count.checked_mul(self.item_size()).ok_or(EOVERFLOW)?;
+        let range_end = offset.checked_add(range_size).ok_or(EOVERFLOW)?;
+
+        if range_end > self.size {
+            return Err(EINVAL);
+        }
+
+        // INVARIANT: Since `offset` and `count` are both in units of `T`, we're guaranteed that the

The use of offset is a little confusing since the function parameter is shadowed by the computed byte offset.

Janne

maintainer
#12 lore raw

Re: [PATCH v6 7/8] rust: Introduce iosys_map bindings

In reply to Lyude Paul

(Cc: Zhi, Matt, Alex)

Quoted 3 lines · Lyude Paul

This introduces a set of bindings for working with iosys_map in rust code. The design of this is heavily based off the design for both the io and dma_map bindings for Rust.

I already had a chat with Lyude about this, but also want to post it here. I have mainly two comment on this:

(1) The backing memory of iosys_map may be a device resource and hence has to be protected against (bus) device / driver unbind.

(2) The idea for the generic I/O infrastructure is to support arbitrary I/O backends rather than only MMIO. For instance, this can also be PCI configuration space, I2C, SPI, etc., but also DMA, VRAM, system memory, etc.

For this, there is a patch series from Zhi [1] splitting up the current I/O structures into traits that we will land soon.

We will also have macros analogous to dma_read!() and dma_write!() for the generic I/O infrastructure, which Matt also works on for his QC SoC driver.

This will allow us to unify all kinds of I/O operations into a single interface, supporting the read!() and write!() accessors for values, the register!() macro and raw accessors, such as e.g. read32().

With this we will have something that is way more powerful than iosys_map and makes this abstraction obsolete.

So instead of introducing an abstraction for iosys_map we should just implement the corresponding I/O backends based on [1].

[1] https://lore.kernel.org/all/[email protected]/

maintainer
#13 lore raw

Re: [PATCH v6 7/8] rust: Introduce iosys_map bindings

Quoted 33 lines · Danilo Krummrich

(Cc: Zhi, Matt, Alex)

Quoted 3 lines · Lyude Paul

This introduces a set of bindings for working with iosys_map in rust code. The design of this is heavily based off the design for both the io and dma_map bindings for Rust.

I already had a chat with Lyude about this, but also want to post it here. I have mainly two comment on this:

(1) The backing memory of iosys_map may be a device resource and hence has to be protected against (bus) device / driver unbind.

(2) The idea for the generic I/O infrastructure is to support arbitrary I/O backends rather than only MMIO. For instance, this can also be PCI configuration space, I2C, SPI, etc., but also DMA, VRAM, system memory, etc.

For this, there is a patch series from Zhi [1] splitting up the current I/O structures into traits that we will land soon.

We will also have macros analogous to dma_read!() and dma_write!() for the generic I/O infrastructure, which Matt also works on for his QC SoC driver.

This will allow us to unify all kinds of I/O operations into a single interface, supporting the read!() and write!() accessors for values, the register!() macro and raw accessors, such as e.g. read32().

Quoted 8 lines

With this we will have something that is way more powerful than iosys_map and makes this abstraction obsolete.

So instead of introducing an abstraction for iosys_map we should just implement the corresponding I/O backends based on [1].

[1] https://lore.kernel.org/all/[email protected]/

I wonder if we can combine this with the idea of splitting I/O ranges that we have been discussing for a while. It is a simple concept: you might have a mapping that covers a given length (say, a single BO), but this mapping might be split into multiple subregions that are not accessed simultaneously. This allows for finer-grain control, as subregions are isolated from each other.

If the idea is to have all these types eventually dereference to “Io” or something like this, the above may be as simple as adding a “split()” method which generates a second Io while simultaneously adjusting the bounds as needed.

If this is a good idea then I can spend cycles, as I’ll have a use for that in Tyr.

— Daniel

maintainer
#14 lore raw

Re: [PATCH v6 7/8] rust: Introduce iosys_map bindings

In reply to Danilo Krummrich
Quoted 25 lines · Danilo Krummrich

(Cc: Zhi, Matt, Alex)

Quoted 3 lines · Lyude Paul

This introduces a set of bindings for working with iosys_map in rust code. The design of this is heavily based off the design for both the io and dma_map bindings for Rust.

I already had a chat with Lyude about this, but also want to post it here. I have mainly two comment on this:

(1) The backing memory of iosys_map may be a device resource and hence has to be protected against (bus) device / driver unbind.

(2) The idea for the generic I/O infrastructure is to support arbitrary I/O backends rather than only MMIO. For instance, this can also be PCI configuration space, I2C, SPI, etc., but also DMA, VRAM, system memory, etc.

For this, there is a patch series from Zhi [1] splitting up the current I/O structures into traits that we will land soon.

We will also have macros analogous to dma_read!() and dma_write!() for the generic I/O infrastructure, which Matt also works on for his QC SoC driver.

Quoted 6 lines

This will allow us to unify all kinds of I/O operations into a single interface, supporting the read!() and write!() accessors for values, the register!() macro and raw accessors, such as e.g. read32().

With this we will have something that is way more powerful than iosys_map and makes this abstraction obsolete.

The trait approach generally requires knowing the target type at compile-time or paying dynamic function calls, but my understanding is that iosys exists because you might not know which variant you want at compile-time.

Perhaps we need an

	enum Iosys {
	    SystemMemory(...),
	    DmaMemory(...),
	}

and implement Io for Iosys in terms of the inner types.

Alice

#15 lore raw

Re: [PATCH v6 7/8] rust: Introduce iosys_map bindings

Quoted 8 lines · Alice Ryhl

The trait approach generally requires knowing the target type at compile-time or paying dynamic function calls, but my understanding is
that iosys exists because you might not know which variant you want at
compile-time.

Quoted 8 lines

Perhaps we need an

	enum Iosys {
	    SystemMemory(...),
	    DmaMemory(...),
	}

and implement Io for Iosys in terms of the inner types.

So - I didn't see this comment until now however: I personally think it can't hurt to have a wrapper around iosys_map simply because it makes it easier to pass back and forth from C APIs, and the other sideaffect of Io is that it's extremely trivial to implement it for the iosys map bindings.

The one downside I could see is that I added C helper functions for all of the iosys map read/write operations which means they aren't currently inlined. But iirc there's work ongoing to allow for these to be inlined so I assume once that is finished there shouldn't be any additional overhead from this approach.

Anyway - will send out the new version in a moment

Quoted 2 lines

Alice

author
#16 lore raw

PATCH v6 8/8 rust: drm/gem: Add vmap functions to shmem bindings

In reply to Lyude Paul

One of the more obvious use cases for gem shmem objects is the ability to create mappings into their contents, specifically iosys mappings. Now that we've added iosys_map rust bindings to the kernel, let's hook these up in gem shmem.

Similar to how we handle SGTables, we make sure there's two different types of mappings: owned mappings (kernel::drm::gem::shmem::VMap) and borrowed mappings (kernel::drm::gem::shmem::VMapRef).

One last note: we change the #[expect(unused)] for RawIoSysMap::from_raw() to an #[allow(unused)]. Normally we would simply remove the lint assertion, however - since shmem is conditionally built, we need allow to avoid hitting warnings in certain kernel configurations.

---
 rust/kernel/drm/gem/shmem.rs | 160 ++++++++++++++++++++++++++++++++++-
 rust/kernel/iosys_map.rs     |   2 +-
 2 files changed, 160 insertions(+), 2 deletions(-)
rust/kernel/drm/gem/shmem.rs +159 -1
index 21ccb6c1824be..62a2c12b9fe2a 100644
@@ -13,15 +13,18 @@
     container_of,
     drm::{device, driver, gem, private::Sealed},
     error::{from_err_ptr, to_result},
+    iosys_map::*,
     prelude::*,
     scatterlist,
+    transmute::*,
     types::{ARef, Opaque},
 };
 use core::{
+    mem::{self, MaybeUninit},
     ops::{Deref, DerefMut},
     ptr::NonNull,
 };
-use gem::{BaseObjectPrivate, DriverObject, IntoGEMObject};
+use gem::{BaseObject, BaseObjectPrivate, DriverObject, IntoGEMObject};
 
 /// A struct for controlling the creation of shmem-backed GEM objects.
 ///
@@ -192,6 +195,72 @@ pub fn owned_sg_table(&self) -> Result<SGTable<T>> {
             _owner: self.into(),
         })
     }
+
+    /// Attempt to create a [`RawIoSysMap`] from the gem object.
+    fn raw_vmap<U: AsBytes + FromBytes>(&self) -> Result<RawIoSysMap<U>> {
+        build_assert!(
+            mem::size_of::<U>() > 0,
+            "It doesn't make sense for the mapping type to be a ZST"
+        );
+
+        let mut map: MaybeUninit<bindings::iosys_map> = MaybeUninit::uninit();
+
+        // SAFETY: drm_gem_shmem_vmap can be called with the DMA reservation lock held
+        to_result(unsafe {
+            // TODO: see top of file
+            bindings::dma_resv_lock(self.raw_dma_resv(), core::ptr::null_mut());
+            let ret = bindings::drm_gem_shmem_vmap_locked(self.as_shmem(), map.as_mut_ptr());
+            bindings::dma_resv_unlock(self.raw_dma_resv());
+            ret
+        })?;
+
+        // SAFETY: if drm_gem_shmem_vmap did not fail, map is initialized now
+        Ok(unsafe { RawIoSysMap::from_raw(map.assume_init()) })
+    }
+
+    /// Unmap a [`RawIoSysMap`] from the gem object.
+    ///
+    /// # Safety
+    ///
+    /// - The caller promises that `map` came from a prior call to [`Self::raw_vmap`] on this gem
+    ///   object.
+    /// - The caller promises that the memory pointed to by `map` will no longer be accesed through
+    ///   this instance.
+    unsafe fn raw_vunmap<U: AsBytes + FromBytes>(&self, map: &mut RawIoSysMap<U>) {
+        let resv = self.raw_dma_resv();
+
+        // SAFETY:
+        // - This function is safe to call with the DMA reservation lock held
+        // - Our `ARef` is proof that the underlying gem object here is initialized and thus safe to
+        //   dereference.
+        unsafe {
+            // TODO: see top of file
+            bindings::dma_resv_lock(resv, core::ptr::null_mut());
+            bindings::drm_gem_shmem_vunmap_locked(self.as_shmem(), map.as_raw_mut());
+            bindings::dma_resv_unlock(resv);
+        }
+    }
+
+    /// Creates and returns a virtual kernel memory mapping for this object.
+    pub fn vmap<U: AsBytes + FromBytes>(&self) -> Result<VMapRef<'_, T, U>> {
+        let map = self.raw_vmap()?;
+
+        Ok(VMapRef {
+            // SAFETY:
+            // - The size of the vmap is the same as the size of the gem
+            // - The vmap will remain alive until this object is dropped.
+            map: unsafe { IoSysMapRef::new(map, self.size()) },
+            owner: self,
+        })
+    }
+
+    /// Creates and returns an owned reference to a virtual kernel memory mapping for this object.
+    pub fn owned_vmap<U: AsBytes + FromBytes>(&self) -> Result<VMap<T, U>> {
+        Ok(VMap {
+            map: self.raw_vmap()?,
+            owner: self.into(),
+        })
+    }
 }
 
 impl<T: DriverObject> Deref for Object<T> {
@@ -243,6 +312,95 @@ impl<T: DriverObject> driver::AllocImpl for Object<T> {
     };
 }
 
+/// A borrowed reference to a virtual mapping for a shmem-based GEM object in kernel address space.
+pub struct VMapRef<'a, D: DriverObject, T: AsBytes + FromBytes> {
+    map: IoSysMapRef<'a, T>,
+    owner: &'a Object<D>,
+}
+
+impl<'a, D: DriverObject, T: AsBytes + FromBytes> Clone for VMapRef<'a, D, T> {
+    fn clone(&self) -> Self {
+        // SAFETY: We have a successful vmap already, so this can't fail
+        unsafe { self.owner.vmap().unwrap_unchecked() }
+    }
+}
+
+impl<'a, D: DriverObject, T: AsBytes + FromBytes> Deref for VMapRef<'a, D, T> {
+    type Target = IoSysMapRef<'a, T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.map
+    }
+}
+
+impl<'a, D: DriverObject, T: AsBytes + FromBytes> DerefMut for VMapRef<'a, D, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.map
+    }
+}
+
+impl<'a, D: DriverObject, T: AsBytes + FromBytes> Drop for VMapRef<'a, D, T> {
+    fn drop(&mut self) {
+        // SAFETY: Our existence is proof that this map was previously created using self.owner.
+        unsafe { self.owner.raw_vunmap(&mut self.map) };
+    }
+}
+
+/// An owned reference to a virtual mapping for a shmem-based GEM object in kernel address space.
+///
+/// # Invariants
+///
+/// - The memory pointed to by `map` is at least as large as `T`.
+/// - The memory pointed to by `map` remains valid at least until this object is dropped.
+pub struct VMap<D: DriverObject, T: AsBytes + FromBytes> {
+    map: RawIoSysMap<T>,
+    owner: ARef<Object<D>>,
+}
+
+impl<D: DriverObject, T: AsBytes + FromBytes> Clone for VMap<D, T> {
+    fn clone(&self) -> Self {
+        // SAFETY: We have a successful vmap already, so this can't fail
+        unsafe { self.owner.owned_vmap().unwrap_unchecked() }
+    }
+}
+
+impl<'a, D: DriverObject, T: AsBytes + FromBytes> From<VMapRef<'a, D, T>> for VMap<D, T> {
+    fn from(value: VMapRef<'a, D, T>) -> Self {
+        let this = Self {
+            map: value.map.clone(),
+            owner: value.owner.into(),
+        };
+
+        mem::forget(value);
+        this
+    }
+}
+
+impl<D: DriverObject, T: AsBytes + FromBytes> VMap<D, T> {
+    /// Return a reference to the iosys map for this `VMap`.
+    pub fn get(&self) -> IoSysMapRef<'_, T> {
+        // SAFETY: The size of the iosys_map is equivalent to the size of the gem object.
+        unsafe { IoSysMapRef::new(self.map.clone(), self.owner.size()) }
+    }
+
+    /// Borrows a reference to the object that owns this virtual mapping.
+    pub fn owner(&self) -> &Object<D> {
+        &self.owner
+    }
+}
+
+impl<D: DriverObject, T: AsBytes + FromBytes> Drop for VMap<D, T> {
+    fn drop(&mut self) {
+        // SAFETY: Our existence is proof that this map was previously created using self.owner
+        unsafe { self.owner.raw_vunmap(&mut self.map) };
+    }
+}
+
+/// SAFETY: `iosys_map` objects are safe to send across threads.
+unsafe impl<D: DriverObject, T: AsBytes + FromBytes> Send for VMap<D, T> {}
+/// SAFETY: `iosys_map` objects are safe to send across threads.
+unsafe impl<D: DriverObject, T: AsBytes + FromBytes> Sync for VMap<D, T> {}
+
 /// An owned reference to a scatter-gather table of DMA address spans for a GEM shmem object.
 ///
 /// This object holds an owned reference to the underlying GEM shmem object, ensuring that the
rust/kernel/iosys_map.rs +1 -1
index 884a3d2be3348..fb1bb1bc03b73 100644
@@ -31,7 +31,7 @@
 
 impl<T: AsBytes + FromBytes> RawIoSysMap<T> {
     /// Convert from a raw `bindings::iosys_map`.
-    #[expect(unused)]
+    #[allow(unused)]
     #[inline]
     pub(crate) fn from_raw(val: bindings::iosys_map) -> Self {
         Self(val, PhantomData)
Signature · 1 line
2.52.0
Tested-by Reviewed-by on the v6 series
#17 lore raw
In reply to Lyude Paul
Quoted 17 lines · Lyude Paul

This is the next version of the shmem backed GEM objects series originally from Asahi, previously posted by Daniel Almeida.

One of the major changes in this patch series is a much better interface around vmaps, which we achieve by introducing a new set of rust bindings for iosys_map.

The previous version of the patch series can be found here:

https://patchwork.freedesktop.org/series/156093/

Changelogs are per-patch

Asahi Lina (2):
rust: helpers: Add bindings/wrappers for dma_resv_lock
rust: drm: gem: shmem: Add DRM shmem helper abstraction

Quoted 7 lines

Lyude Paul (6):
rust/drm: Add gem::impl_aref_for_gem_obj!
rust: drm: gem: Add raw_dma_resv() function
rust: gem: Introduce DriverObject::Args
rust: drm: gem: Introduce shmem::SGTable
rust: Introduce iosys_map bindings
rust: drm/gem: Add vmap functions to shmem bindings

whole series is tested with the asahi (Apple silicon GPU driver) on top of v6.18 plus further modifications.

Patches I did not reply to directly are

Janne