From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 9CDA740D58A; Sat, 20 Jun 2026 00:59:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781917182; cv=none; b=T6aVV6IyJlRly8YeT7+fSkPX6j4+WLgqEozO56gWkpBQ2Of3rR0JEhBc6ItorxkIdTlHzn+WOMIj42/QaeoAIS8BeNcoxNe/BWzVYk5IdQdK7z3TprKIwyFKNP4j1Tik+MmYfOzPsBUDD5E+bWXRor29GSQVdm5MRwaUcUy+cag= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781917182; c=relaxed/simple; bh=DgG2vc6ciKhoiTHeKBkSxsbrLilgrVeIyd45VjAxhC4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=C7+j9u+GndOY3Setri3l6AgETnH6oT/g5Q9GKZYtYGRs9RiDHKTpM+4F4qw9Ow3RUlGsa+nZlKWni/bDWNBUdygqsQDqjHGP5ZvGWDRZrH2LWXLNQlKdjijd/+eN/NDjj5d7DOXZmA+7AhnzL2bcv9FWCpNm3APTM9tag6MGGT0= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=dYztD8Hj; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="dYztD8Hj" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3D2AA1F00A3A; Sat, 20 Jun 2026 00:59:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1781917181; bh=OIKBr1i4T/n3+7dsoij726Xq6HWOrPSKiM18XbY403M=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=dYztD8HjAGUyt5EGhS9MbGmFteVT5/Sm6Two2wmKSGNjQayim5x+UKpnpX65eZ8yz cxJfOFpTuabxUcx1FWxQpZ3idrzrrmV6eIcCFKNvM/7hjdK0y0YX63kNY5iO+g3rTy hMrPjvozI5Pi7XPLlDBYFFb24B75Usp4xk3RMQ+8hAawjVQYPFXKGwp+t2AS1FLkqu x9PyfPJ7cuLiLEtRZih80MCCrJmz5o+MGqzfVXtJLLTOXJU7DTAaN0S9y3eba5i0jR QOeR8XtsMZpgA11q0Kk+I+9g5VaSTiz0cBItYhR9r83CriJ7FjHag0OmF0GPkyYbWS Fik+0oVKcRyZA== From: Danilo Krummrich To: dakr@kernel.org, aliceryhl@google.com, daniel.almeida@collabora.com, acourbot@nvidia.com, ecourtney@nvidia.com, ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, tmgross@umich.edu, deborah.brouwer@collabora.com, boris.brezillon@collabora.com, lyude@redhat.com Cc: driver-core@lists.linux.dev, linux-kernel@vger.kernel.org, nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org, rust-for-linux@vger.kernel.org Subject: [PATCH v3 09/13] rust: drm: Add RegistrationGuard for drm_dev_enter/exit critical sections Date: Sat, 20 Jun 2026 02:51:21 +0200 Message-ID: <20260620005431.1562115-10-dakr@kernel.org> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260620005431.1562115-1-dakr@kernel.org> References: <20260620005431.1562115-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 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. The guard is obtained from Device (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 --- 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` dereferences to `Device` ([`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`. 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(&self) -> &Device Device { + /// 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> { + 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`], 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, + idx: i32, +} + +impl Deref for RegistrationGuard<'_, T> { + type Target = Device; + + #[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 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 Deref for Device { 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 Send for Registration {} impl Drop for Registration { 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>`. 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