Message ID | 20250410235546.43736-6-dakr@kernel.org (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | DRM Rust abstractions | expand |
Reviewed-by: Alyssa Rosenzweig <alyssa@rosenzweig.io>
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 --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 {}