diff mbox series

[v2,5/8] rust: drm: add DRM driver registration

Message ID 20250410235546.43736-6-dakr@kernel.org (mailing list archive)
State New
Headers show
Series DRM Rust abstractions | expand

Commit Message

Danilo Krummrich April 10, 2025, 11:55 p.m. UTC
From: Asahi Lina <lina@asahilina.net>

Implement the DRM driver `Registration`.

The `Registration` structure is responsible to register and unregister a
DRM driver. It makes use of the `Devres` container in order to allow the
`Registration` to be owned by devres, such that it is automatically
dropped (and the DRM driver unregistered) once the parent device is
unbound.

Signed-off-by: Asahi Lina <lina@asahilina.net>
[ Rework of drm::Registration
    * move VTABLE to drm::Device to prevent use-after-free bugs; VTABLE
      needs to be bound to the lifetime of drm::Device, not the
      drm::Registration
    * combine new() and register() to get rid of the registered boolean
    * remove file_operations
    * move struct drm_device creation to drm::Device
    * introduce Devres
    * original source archive: https://archive.is/Pl9ys

  - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
 rust/kernel/drm/driver.rs | 60 ++++++++++++++++++++++++++++++++++++++-
 rust/kernel/drm/mod.rs    |  1 +
 2 files changed, 60 insertions(+), 1 deletion(-)

Comments

Alyssa Rosenzweig April 14, 2025, 2:11 p.m. UTC | #1
Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
Lyude Paul April 18, 2025, 9:12 p.m. UTC | #2
On Fri, 2025-04-11 at 01:55 +0200, Danilo Krummrich wrote:
> From: Asahi Lina <lina@asahilina.net>
> 
> Implement the DRM driver `Registration`.
> 
> The `Registration` structure is responsible to register and unregister a
> DRM driver. It makes use of the `Devres` container in order to allow the
> `Registration` to be owned by devres, such that it is automatically
> dropped (and the DRM driver unregistered) once the parent device is
> unbound.
> 
> Signed-off-by: Asahi Lina <lina@asahilina.net>
> [ Rework of drm::Registration
>     * move VTABLE to drm::Device to prevent use-after-free bugs; VTABLE
>       needs to be bound to the lifetime of drm::Device, not the
>       drm::Registration
>     * combine new() and register() to get rid of the registered boolean
>     * remove file_operations
>     * move struct drm_device creation to drm::Device
>     * introduce Devres
>     * original source archive: https://archive.is/Pl9ys
> 
>   - Danilo ]
> Signed-off-by: Danilo Krummrich <dakr@kernel.org>
> ---
>  rust/kernel/drm/driver.rs | 60 ++++++++++++++++++++++++++++++++++++++-
>  rust/kernel/drm/mod.rs    |  1 +
>  2 files changed, 60 insertions(+), 1 deletion(-)
> 
> diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
> index 6d09d1933d3e..96bb287eada2 100644
> --- a/rust/kernel/drm/driver.rs
> +++ b/rust/kernel/drm/driver.rs
> @@ -4,7 +4,15 @@
>  //!
>  //! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/drm_drv.h)
>  
> -use crate::{bindings, drm, str::CStr};
> +use crate::{
> +    bindings, device,
> +    devres::Devres,
> +    drm,
> +    error::{Error, Result},
> +    prelude::*,
> +    str::CStr,
> +    types::ARef,
> +};
>  use macros::vtable;
>  
>  /// Driver use the GEM memory manager. This should be set for all modern drivers.
> @@ -107,3 +115,53 @@ pub trait Driver {
>      /// IOCTL list. See `kernel::drm::ioctl::declare_drm_ioctls!{}`.
>      const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor];
>  }
> +
> +/// The registration type of a `drm::Device`.
> +///
> +/// Once the `Registration` structure is dropped, the device is unregistered.
> +pub struct Registration<T: Driver>(ARef<drm::Device<T>>);
> +
> +impl<T: Driver> Registration<T> {
> +    /// Creates a new [`Registration`] and registers it.
> +    pub fn new(drm: &drm::Device<T>, flags: usize) -> Result<Self> {

We should probably review whether we want `flags` here at some point

> +        // SAFETY: Safe by the invariants of `drm::Device`.
> +        let ret = unsafe { bindings::drm_dev_register(drm.as_raw(), flags) };
> +        if ret < 0 {
> +            return Err(Error::from_errno(ret));
> +        }
> +
> +        Ok(Self(drm.into()))

This could just be:

        // SAFETY: Safe by the invariants of `drm::Device`.
        to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags) })?;

        Ok(Self(drm.into()))


> +    }
> +
> +    /// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to
> +    /// [`Devres`].
> +    pub fn new_foreign_owned(drm: &drm::Device<T>, dev: &device::Device, flags: usize) -> Result {
> +        if drm.as_ref().as_raw() != dev.as_raw() {
> +            return Err(EINVAL);
> +        }
> +
> +        let reg = Registration::<T>::new(drm, flags)?;
> +        Devres::new_foreign_owned(dev, reg, GFP_KERNEL)
> +    }
> +
> +    /// Returns a reference to the `Device` instance for this registration.
> +    pub fn device(&self) -> &drm::Device<T> {
> +        &self.0
> +    }
> +}
> +
> +// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between
> +// threads, hence it's safe to share it.
> +unsafe impl<T: Driver> Sync for Registration<T> {}
> +
> +// SAFETY: Registration with and unregistration from the DRM subsystem can happen from any thread.
> +unsafe impl<T: Driver> Send for Registration<T> {}
> +
> +impl<T: Driver> Drop for Registration<T> {
> +    /// Removes the registration from the kernel if it has completed successfully before.

Probably want to drop this comment, since it is impossible for the
registration to have not completed successfully by this point

> +    fn drop(&mut self) {
> +        // SAFETY: Safe by the invariant of `ARef<drm::Device<T>>`. The existence of this
> +        // `Registration` also guarantees the this `drm::Device` is actually registered.
> +        unsafe { bindings::drm_dev_unregister(self.0.as_raw()) };
> +    }
> +}
> diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs
> index 967854a2083e..2d88e70ba607 100644
> --- a/rust/kernel/drm/mod.rs
> +++ b/rust/kernel/drm/mod.rs
> @@ -9,6 +9,7 @@
>  pub use self::device::Device;
>  pub use self::driver::Driver;
>  pub use self::driver::DriverInfo;
> +pub use self::driver::Registration;
>  
>  pub(crate) mod private {
>      pub trait Sealed {}
diff mbox series

Patch

diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 6d09d1933d3e..96bb287eada2 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -4,7 +4,15 @@ 
 //!
 //! C header: [`include/linux/drm/drm_drv.h`](srctree/include/linux/drm/drm_drv.h)
 
-use crate::{bindings, drm, str::CStr};
+use crate::{
+    bindings, device,
+    devres::Devres,
+    drm,
+    error::{Error, Result},
+    prelude::*,
+    str::CStr,
+    types::ARef,
+};
 use macros::vtable;
 
 /// Driver use the GEM memory manager. This should be set for all modern drivers.
@@ -107,3 +115,53 @@  pub trait Driver {
     /// IOCTL list. See `kernel::drm::ioctl::declare_drm_ioctls!{}`.
     const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor];
 }
+
+/// The registration type of a `drm::Device`.
+///
+/// Once the `Registration` structure is dropped, the device is unregistered.
+pub struct Registration<T: Driver>(ARef<drm::Device<T>>);
+
+impl<T: Driver> Registration<T> {
+    /// Creates a new [`Registration`] and registers it.
+    pub fn new(drm: &drm::Device<T>, flags: usize) -> Result<Self> {
+        // SAFETY: Safe by the invariants of `drm::Device`.
+        let ret = unsafe { bindings::drm_dev_register(drm.as_raw(), flags) };
+        if ret < 0 {
+            return Err(Error::from_errno(ret));
+        }
+
+        Ok(Self(drm.into()))
+    }
+
+    /// Same as [`Registration::new`}, but transfers ownership of the [`Registration`] to
+    /// [`Devres`].
+    pub fn new_foreign_owned(drm: &drm::Device<T>, dev: &device::Device, flags: usize) -> Result {
+        if drm.as_ref().as_raw() != dev.as_raw() {
+            return Err(EINVAL);
+        }
+
+        let reg = Registration::<T>::new(drm, flags)?;
+        Devres::new_foreign_owned(dev, reg, GFP_KERNEL)
+    }
+
+    /// Returns a reference to the `Device` instance for this registration.
+    pub fn device(&self) -> &drm::Device<T> {
+        &self.0
+    }
+}
+
+// SAFETY: `Registration` doesn't offer any methods or access to fields when shared between
+// threads, hence it's safe to share it.
+unsafe impl<T: Driver> Sync for Registration<T> {}
+
+// SAFETY: Registration with and unregistration from the DRM subsystem can happen from any thread.
+unsafe impl<T: Driver> Send for Registration<T> {}
+
+impl<T: Driver> Drop for Registration<T> {
+    /// Removes the registration from the kernel if it has completed successfully before.
+    fn drop(&mut self) {
+        // SAFETY: Safe by the invariant of `ARef<drm::Device<T>>`. The existence of this
+        // `Registration` also guarantees the this `drm::Device` is actually registered.
+        unsafe { bindings::drm_dev_unregister(self.0.as_raw()) };
+    }
+}
diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs
index 967854a2083e..2d88e70ba607 100644
--- a/rust/kernel/drm/mod.rs
+++ b/rust/kernel/drm/mod.rs
@@ -9,6 +9,7 @@ 
 pub use self::device::Device;
 pub use self::driver::Driver;
 pub use self::driver::DriverInfo;
+pub use self::driver::Registration;
 
 pub(crate) mod private {
     pub trait Sealed {}