[WIP RFC v2 04/35] rust: drm/kms: Introduce the main ModeConfigObject traits

Daniel Almeida daniel.almeida at collabora.com
Tue Nov 26 20:34:50 UTC 2024


Hi Lyude,

> On 30 Sep 2024, at 20:09, Lyude Paul <lyude at redhat.com> wrote:
> 
> The KMS API has a very consistent idea of a "mode config object", which
> includes any object with a drm_mode_object struct embedded in it. These
> objects have their own object IDs which DRM exposes to userspace, and we
> introduce the ModeConfigObject trait to represent any object matching these
> characteristics.
> 
> One slightly less consistent trait of these objects however: some mode
> objects have a reference count, while others don't. Since rust requires
> that we are able to define the lifetime of an object up-front, we introduce
> two other super-traits of ModeConfigObject for this:
> 
> * StaticModeObject - this trait represents any mode object which does not
>  have a reference count of its own. Such objects can be considered to
>  share the lifetime of their parent KMS device
> * RcModeObject - this trait represents any mode object which does have its
>  own reference count. Objects implementing this trait get a free blanket
>  implementation of AlwaysRefCounted, and as such can be used with the ARef
>  container without us having to implement AlwaysRefCounted for each
>  individual mode object.
> 
> This will be able to handle most lifetimes we'll need with one exception:
> it's entirely possible a driver may want to hold a "owned" reference to a
> static mode object. We allow for this by introducing the KmsRef container,
> which grabs an owned refcount to the parent KMS device of a
> StaticModeObject and holds a pointer to said object - essentially allowing
> it to act identically to an owned refcount by preventing the device's
> lifetime from ending until the KmsRef is dropped. I choose not to use
> AlwaysRefCounted for this as holding a refcount to the device has its own
> set of implications since if you forget to drop the KmsRef the device will
> never be destroyed.
> 
> Signed-off-by: Lyude Paul <lyude at redhat.com>
> ---
> rust/bindings/bindings_helper.h |   1 +
> rust/kernel/drm/kms.rs          | 107 ++++++++++++++++++++++++++++++++
> 2 files changed, 108 insertions(+)
> 
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 9803e0ecac7c1..ba1871b05b7fa 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -17,6 +17,7 @@
> #include <drm/drm_gem.h>
> #include <drm/drm_gem_framebuffer_helper.h>
> #include <drm/drm_gem_shmem_helper.h>
> +#include <drm/drm_mode_object.h>
> #include <drm/drm_ioctl.h>
> #include <kunit/test.h>
> #include <linux/blk-mq.h>
> diff --git a/rust/kernel/drm/kms.rs b/rust/kernel/drm/kms.rs
> index d3558a5eccc54..f1a8ba4b7e296 100644
> --- a/rust/kernel/drm/kms.rs
> +++ b/rust/kernel/drm/kms.rs
> @@ -228,3 +228,110 @@ impl<T, K> KmsDriver for T
> where
>     T: Driver<Kms = K>,
>     K: Kms<Driver = T> {}
> +
> +/// A modesetting object in DRM.
> +///
> +/// This is any type of object where the underlying C object contains a [`struct drm_mode_object`].
> +///
> +/// [`struct drm_mode_object`]: srctree/include/drm/drm_mode_object.h
> +pub trait ModeObject: Sealed + Send + Sync {

Can you briefly document why these bounds are needed?

> +    /// The parent driver for this [`ModeObject`].
> +    type Driver: KmsDriver;
> +
> +    /// Return the [`Device`] for this [`ModeObject`].
> +    fn drm_dev(&self) -> &Device<Self::Driver>;
> +
> +    /// Return a pointer to the [`struct drm_mode_object`] for this [`ModeObject`].
> +    ///
> +    /// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h)
> +    fn raw_mode_obj(&self) -> *mut bindings::drm_mode_object;
> +}
> +
> +/// A trait for modesetting objects which don't come with their own reference-counting.
> +///
> +/// Some [`ModeObject`] types in DRM do not have a reference count. These types are considered
> +/// "static" and share the lifetime of their parent [`Device`]. To retrieve an owned reference to
> +/// such types, see [`KmsRef`].
> +///
> +/// # Safety
> +///
> +/// This trait must only be implemented for modesetting objects which do not have a refcount within
> +/// their [`struct drm_mode_object`], otherwise [`KmsRef`] can't guarantee the object will stay
> +/// alive.
> +///
> +/// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h)
> +pub unsafe trait StaticModeObject: ModeObject {}
> +
> +/// An owned reference to a [`StaticModeObject`].
> +///
> +/// Note that since [`StaticModeObject`] types share the lifetime of their parent [`Device`], the
> +/// parent [`Device`] will stay alive as long as this type exists. Thus, users should be aware that
> +/// storing a [`KmsRef`] within a [`ModeObject`] is a circular reference.
> +///
> +/// # Invariants
> +///
> +/// `self.0` points to a valid instance of `T` throughout the lifetime of this type.
> +pub struct KmsRef<T: StaticModeObject>(NonNull<T>);
> +
> +// SAFETY: Owned references to DRM device are thread-safe.
> +unsafe impl<T: StaticModeObject> Send for KmsRef<T> {}
> +unsafe impl<T: StaticModeObject> Sync for KmsRef<T> {}
> +
> +impl<T: StaticModeObject> From<&T> for KmsRef<T> {
> +    fn from(value: &T) -> Self {
> +        // We will drop the reference we leak here in Drop
> +        value.drm_dev().inc_ref();
> +
> +        Self(value.into())
> +    }
> +}
> +
> +impl<T: StaticModeObject> Drop for KmsRef<T> {
> +    fn drop(&mut self) {
> +        // SAFETY: We're reclaiming the reference we leaked in From<&T>
> +        drop(unsafe { ARef::from_raw(self.drm_dev().into()) })
> +    }
> +}
> +
> +impl<T: StaticModeObject> Deref for KmsRef<T> {
> +    type Target = T;
> +
> +    fn deref(&self) -> &Self::Target {
> +        // SAFETY: We're guaranteed object will point to a valid object as long as we hold dev
> +        unsafe { self.0.as_ref() }
> +    }
> +}
> +
> +impl<T: StaticModeObject> Clone for KmsRef<T> {
> +    fn clone(&self) -> Self {
> +        self.drm_dev().inc_ref();
> +
> +        Self(self.0)
> +    }
> +}
> +
> +/// A trait for [`ModeObject`] which is reference counted.
> +///
> +/// This trait is implemented by DRM for any [`ModeObject`] which has a reference count provided by
> +/// [`struct drm_mode_object`]. It provides a common implementation of [`AlwaysRefCounted`], since
> +/// all [`RcModeObject`] types use the same functions for refcounting.
> +///
> +/// # Safety
> +///
> +/// The [`ModeObject`] must initialize the refcount in its [`struct drm_mode_object`] field.
> +///
> +/// [`struct drm_mode_object`]: (srctree/include/drm/drm_mode_object.h)
> +pub unsafe trait RcModeObject: ModeObject {}
> +
> +unsafe impl<T: RcModeObject> AlwaysRefCounted for T {
> +    fn inc_ref(&self) {
> +        // SAFETY: FFI call with no special requirements
> +        unsafe { bindings::drm_mode_object_get(self.raw_mode_obj()) }

Well, at least the pointer has to be valid. I assume that passing core::ptr::null_mut() here will crash,
for example. Also, do we have to worry about races? T is Sync, so I assume you mean to have
this call reachable from multiple threads.

The kref docs seem to indicate this is not a problem:

```
This way, it doesn’t matter what order the two threads handle the data, the kref_put() handles knowing when the data is not referenced any more and releasing it. The kref_get() does not require a lock, since we already have a valid pointer that we own a refcount for. The put needs no lock because nothing tries to get the data without already holding a pointer.
```

Regardless, IMHO it’s good to document it here as well.
 
> +    }
> +
> +    unsafe fn dec_ref(obj: ptr::NonNull<Self>) {
> +        // SAFETY: We never expose modesetting objects in our interfaces to users before they're
> +        // initialized
> +        unsafe { bindings::drm_mode_object_put(obj.as_ref().raw_mode_obj()) }

Same here, pointer must be valid.

> +    }
> +}
> -- 
> 2.46.1
> 
> 

— Daniel



More information about the dri-devel mailing list