[PATCH v3 09/13] rust: drm: Add RegistrationGuard for drm_dev_enter/exit critical sections
From: Danilo Krummrich
Date: Fri Jun 19 2026 - 21:02:43 EST
DRM ioctls do not guarantee that the parent bus device is still bound.
However, since DRM device registration is managed through Devres, using
drm_dev_unplug() on unregistration ensures that between drm_dev_enter()
and drm_dev_exit() the parent device must be bound.
Add RegistrationGuard, a guard object representing a drm_dev_enter/exit
SRCU critical section that dereferences to &Device<T, Registered>. The
guard is obtained from Device<T> (Normal) and proves at runtime that the
device is still registered.
Switch Registration::drop from drm_dev_unregister() to drm_dev_unplug()
to provide the SRCU barrier that RegistrationGuard's safety argument
relies on.
Signed-off-by: Danilo Krummrich <dakr@xxxxxxxxxx>
---
rust/kernel/drm/device.rs | 76 +++++++++++++++++++++++++++++++++------
rust/kernel/drm/driver.rs | 10 ++++--
rust/kernel/drm/mod.rs | 1 +
3 files changed, 74 insertions(+), 13 deletions(-)
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 885077e270eb..bc8cfd753596 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -78,7 +78,8 @@ macro_rules! drm_legacy_fields {
///
/// - [`Normal`]: The general-purpose, reference-counted context. A [`Device`] in this context may
/// or may not be registered with userspace.
-/// - [`Registered`]: The device has been registered with userspace at some point.
+/// - [`Registered`]: The device is currently registered with userspace and the parent bus device
+/// is bound.
///
/// `Device<T, Registered>` dereferences to `Device<T>` ([`Normal`]), so any method available on a
/// [`Normal`] device is also available on a [`Registered`] one.
@@ -96,18 +97,15 @@ pub trait DeviceContext: Sealed + Send + Sync {}
impl Sealed for Normal {}
impl DeviceContext for Normal {}
-/// The [`DeviceContext`] of a [`Device`] that was registered with userspace at some point.
+/// The [`DeviceContext`] of a [`Device`] that is currently registered with userspace.
///
-/// This represents a [`Device`] which is guaranteed to have been registered with userspace at
-/// some point in time. Such a DRM device is guaranteed to have been fully-initialized.
-///
-/// Note: A device in this context is not guaranteed to remain registered with userspace for its
-/// entire lifetime, as this is impossible to guarantee at compile-time.
+/// A [`Device`] in this context is guaranteed to be registered and its parent bus device is
+/// guaranteed to be bound. This is enforced at runtime by [`RegistrationGuard`], which holds a
+/// `drm_dev_enter()` / `drm_dev_exit()` SRCU critical section.
///
/// # Invariants
///
-/// A [`Device`] in this [`DeviceContext`] is guaranteed to have been registered with userspace
-/// at some point in time.
+/// The parent bus device is bound for the duration of any reference to a `Device<T, Registered>`.
pub struct Registered;
impl Sealed for Registered {}
@@ -243,8 +241,8 @@ pub fn new(
/// A typed DRM device with a specific [`drm::Driver`] implementation and [`DeviceContext`].
///
-/// A device in the [`Registered`] context is guaranteed to have been registered with userspace
-/// at some point. The [`Normal`] context is the general-purpose, reference-counted context.
+/// A device in the [`Registered`] context is currently registered with userspace and its parent
+/// bus device is bound. The [`Normal`] context is the general-purpose, reference-counted context.
///
/// # Invariants
///
@@ -323,6 +321,62 @@ pub(crate) unsafe fn assume_ctx<NewCtx: DeviceContext>(&self) -> &Device<T, NewC
}
}
+impl<T: drm::Driver> Device<T> {
+ /// Guard against the parent bus device being unbound.
+ ///
+ /// Returns a [`RegistrationGuard`] if the device has not been unplugged, [`None`] otherwise.
+ ///
+ /// While [`RegistrationGuard`] is held the parent device is guaranteed to be bound.
+ #[must_use]
+ pub fn registration_guard(&self) -> Option<RegistrationGuard<'_, T>> {
+ let mut idx: i32 = 0;
+ // SAFETY: `self.as_raw()` is a valid pointer to a `struct drm_device`.
+ if unsafe { bindings::drm_dev_enter(self.as_raw(), &mut idx) } {
+ Some(RegistrationGuard { dev: self, idx })
+ } else {
+ None
+ }
+ }
+}
+
+/// A guard proving the DRM device is registered and the parent bus device is bound.
+///
+/// The guard dereferences to [`Device<T, Registered>`], providing access to the DRM device with
+/// the guarantee that the parent bus device is bound for the entire duration of the critical
+/// section.
+///
+/// Internally this is backed by a `drm_dev_enter()` / `drm_dev_exit()` SRCU critical section.
+///
+/// # Invariants
+///
+/// - `idx` is the SRCU read lock index returned by a successful `drm_dev_enter()` call.
+/// - The parent bus device of `dev` is bound for the lifetime of this guard.
+#[must_use]
+pub struct RegistrationGuard<'a, T: drm::Driver> {
+ dev: &'a Device<T>,
+ idx: i32,
+}
+
+impl<T: drm::Driver> Deref for RegistrationGuard<'_, T> {
+ type Target = Device<T, Registered>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: A successful `drm_dev_enter()` guarantees that the device is registered and
+ // the parent bus device is bound.
+ unsafe { self.dev.assume_ctx() }
+ }
+}
+
+impl<T: drm::Driver> Drop for RegistrationGuard<'_, T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: `self.idx` was returned by a successful `drm_dev_enter()` call, as guaranteed
+ // by the type invariants of `RegistrationGuard`.
+ unsafe { bindings::drm_dev_exit(self.idx) };
+ }
+}
+
impl<T: drm::Driver> Deref for Device<T> {
type Target = T::Data;
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 9ba7dc3a7a97..ceb2829985c7 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -199,8 +199,14 @@ unsafe impl<T: Driver> Send for Registration<T> {}
impl<T: Driver> Drop for Registration<T> {
fn drop(&mut self) {
+ // Use `drm_dev_unplug` rather than `drm_dev_unregister` to ensure that existing
+ // `drm_dev_enter()` critical sections complete before unregistration proceeds. This
+ // is required for the safety of `RegistrationGuard`, which relies on the SRCU barrier in
+ // `drm_dev_unplug()` to guarantee that the parent device is still bound within the
+ // critical section.
+ //
// 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()) };
+ // `Registration` also guarantees that this `drm::Device` is actually registered.
+ unsafe { bindings::drm_dev_unplug(self.0.as_raw()) };
}
}
diff --git a/rust/kernel/drm/mod.rs b/rust/kernel/drm/mod.rs
index e5bfaf130342..6cfb01eec7ee 100644
--- a/rust/kernel/drm/mod.rs
+++ b/rust/kernel/drm/mod.rs
@@ -13,6 +13,7 @@
pub use self::device::DeviceContext;
pub use self::device::Normal;
pub use self::device::Registered;
+pub use self::device::RegistrationGuard;
pub use self::device::UnregisteredDevice;
pub use self::driver::Driver;
pub use self::driver::DriverInfo;
--
2.54.0