From: Lyude Paul <lyude@redhat.com>
To: linux-kernel@vger.kernel.org, Danilo Krummrich <dakr@kernel.org>,
rust-for-linux@vger.kernel.org, dri-devel@lists.freedesktop.org
Cc: Daniel Almeida <daniel.almeida@collabora.com>,
nouveau@lists.freedesktop.org, "Gary Guo" <gary@garyguo.net>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Simona Vetter" <simona@ffwll.ch>,
"Alice Ryhl" <aliceryhl@google.com>,
"Shankari Anand" <shankari.ak0208@gmail.com>,
"David Airlie" <airlied@gmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Asahi Lina" <lina+kernel@asahilina.net>,
"Lyude Paul" <lyude@redhat.com>
Subject: [PATCH v5 2/4] rust/drm: Don't setup private driver data until registration
Date: Tue, 17 Mar 2026 17:28:31 -0400 [thread overview]
Message-ID: <20260317213003.606088-3-lyude@redhat.com> (raw)
In-Reply-To: <20260317213003.606088-1-lyude@redhat.com>
Now that we have a DeviceContext that we can use to represent whether a
Device is known to have been registered, we can make it so that drivers can
create their Devices but wait until the registration phase to assign their
private data to the Device. This is desirable as some drivers need to make
use of the DRM device early on before finalizing their private driver data.
As such, this change makes it so that the driver's private data can
currently only be accessed through Device<T, Registered> types and not
Device<T, Uninit>.
Signed-off-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
---
V4:
* Remove accidental double-aliasing &mut in Device::release()
V5:
* Change new members of Device to pub(super)
Signed-off-by: Lyude Paul <lyude@redhat.com>
---
drivers/gpu/drm/nova/driver.rs | 4 +--
drivers/gpu/drm/tyr/driver.rs | 4 +--
rust/kernel/drm/device.rs | 64 +++++++++++++++++++++-------------
rust/kernel/drm/driver.rs | 19 ++++++++--
4 files changed, 59 insertions(+), 32 deletions(-)
diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs
index 99d6841b69cbc..8cea5f68c3b04 100644
--- a/drivers/gpu/drm/nova/driver.rs
+++ b/drivers/gpu/drm/nova/driver.rs
@@ -56,8 +56,8 @@ impl auxiliary::Driver for NovaDriver {
fn probe(adev: &auxiliary::Device<Core>, _info: &Self::IdInfo) -> impl PinInit<Self, Error> {
let data = try_pin_init!(NovaData { adev: adev.into() });
- let drm = drm::UnregisteredDevice::<Self>::new(adev.as_ref(), data)?;
- let drm = drm::Registration::new_foreign_owned(drm, adev.as_ref(), 0)?;
+ let drm = drm::UnregisteredDevice::<Self>::new(adev.as_ref())?;
+ let drm = drm::Registration::new_foreign_owned(drm, adev.as_ref(), data, 0)?;
Ok(Self { drm: drm.into() })
}
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 6238f6e2b3bd2..76c5aed1171b1 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -145,8 +145,8 @@ fn probe(
gpu_info,
});
- let tdev = drm::UnregisteredDevice::<TyrDrmDriver>::new(pdev.as_ref(), data)?;
- let tdev = drm::driver::Registration::new_foreign_owned(tdev, pdev.as_ref(), 0)?;
+ let tdev = drm::UnregisteredDevice::<TyrDrmDriver>::new(pdev.as_ref())?;
+ let tdev = drm::driver::Registration::new_foreign_owned(tdev, pdev.as_ref(), data, 0)?;
let driver = TyrPlatformDriverData {
_device: tdev.into(),
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index b47dd9c29d578..74321fb6a9fab 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -26,13 +26,15 @@
};
use core::{
alloc::Layout,
+ cell::UnsafeCell,
marker::PhantomData,
- mem,
- ops::Deref,
- ptr::{
+ mem::{
self,
- NonNull, //
+ MaybeUninit, //
},
+ ops::Deref,
+ ptr::NonNull,
+ sync::atomic::*,
};
#[cfg(CONFIG_DRM_LEGACY)]
@@ -141,7 +143,7 @@ impl DeviceContext for Uninit {}
///
/// The device in `self.0` is guaranteed to be a newly created [`Device`] that has not yet been
/// registered with userspace until this type is dropped.
-pub struct UnregisteredDevice<T: drm::Driver>(ARef<Device<T, Uninit>>, NotThreadSafe);
+pub struct UnregisteredDevice<T: drm::Driver>(pub(crate) ARef<Device<T, Uninit>>, NotThreadSafe);
impl<T: drm::Driver> Deref for UnregisteredDevice<T> {
type Target = Device<T, Uninit>;
@@ -188,7 +190,7 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
/// Create a new `UnregisteredDevice` for a `drm::Driver`.
///
/// This can be used to create a [`Registration`](kernel::drm::Registration).
- pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<Self> {
+ pub fn new(dev: &device::Device) -> Result<Self> {
// `__drm_dev_alloc` uses `kmalloc()` to allocate memory, hence ensure a `kmalloc()`
// compatible `Layout`.
let layout = Kmalloc::aligned_layout(Layout::new::<Device<T, Uninit>>());
@@ -207,22 +209,6 @@ pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<S
.cast();
let raw_drm = NonNull::new(from_err_ptr(raw_drm)?).ok_or(ENOMEM)?;
- // SAFETY: `raw_drm` is a valid pointer to `Self`.
- let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) };
-
- // SAFETY:
- // - `raw_data` is a valid pointer to uninitialized memory.
- // - `raw_data` will not move until it is dropped.
- unsafe { data.__pinned_init(raw_data) }.inspect_err(|_| {
- // SAFETY: `raw_drm` is a valid pointer to `Self`, given that `__drm_dev_alloc` was
- // successful.
- let drm_dev = unsafe { Device::into_drm_device(raw_drm) };
-
- // SAFETY: `__drm_dev_alloc()` was successful, hence `drm_dev` must be valid and the
- // refcount must be non-zero.
- unsafe { bindings::drm_dev_put(drm_dev) };
- })?;
-
// SAFETY: The reference count is one, and now we take ownership of that reference as a
// `drm::Device`.
// INVARIANT: We just created the device above, but have yet to call `drm_dev_register`.
@@ -253,7 +239,15 @@ pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<S
#[repr(C)]
pub struct Device<T: drm::Driver, C: DeviceContext = Registered> {
dev: Opaque<bindings::drm_device>,
- data: T::Data,
+
+ /// Keeps track of whether we've initialized the device data yet.
+ pub(super) data_is_init: AtomicBool,
+
+ /// The Driver's private data.
+ ///
+ /// This must only be written to from [`drm::Registration::new`].
+ pub(super) data: UnsafeCell<MaybeUninit<T::Data>>,
+
_ctx: PhantomData<C>,
}
@@ -304,6 +298,22 @@ extern "C" fn release(ptr: *mut bindings::drm_device) {
// SAFETY: `ptr` is a valid pointer to a `struct drm_device` and embedded in `Self`.
let this = unsafe { Self::from_drm_device(ptr) };
+ // SAFETY:
+ // - Since we are in release(), we are guaranteed that no one else has access to `this`.
+ // - We confirmed above that `this` is a valid pointer to an initialized `Self`.
+ let is_init = unsafe { &*this }.data_is_init.load(Ordering::Relaxed);
+ if is_init {
+ // SAFETY:
+ // - We confirmed we have unique access to this above.
+ // - We confirmed that `data` is initialized above.
+ let data_ptr = unsafe { &mut (*this).data };
+
+ // SAFETY:
+ // - We checked that the data is initialized above.
+ // - We do not use `data` any point after calling this function.
+ unsafe { data_ptr.get_mut().assume_init_drop() };
+ }
+
// SAFETY:
// - When `release` runs it is guaranteed that there is no further access to `this`.
// - `this` is valid for dropping.
@@ -322,11 +332,15 @@ pub(crate) unsafe fn assume_ctx<NewCtx: DeviceContext>(&self) -> &Device<T, NewC
}
}
-impl<T: drm::Driver, C: DeviceContext> Deref for Device<T, C> {
+impl<T: drm::Driver> Deref for Device<T, Registered> {
type Target = T::Data;
fn deref(&self) -> &Self::Target {
- &self.data
+ // SAFETY:
+ // - `data` is initialized before any `Device`s with the `Registered` context are available
+ // to the user.
+ // - `data` is only written to once in `Registration::new()`, so this read will never race.
+ unsafe { (&*self.data.get()).assume_init_ref() }
}
}
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 55b01ee088548..cfb8884ece700 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -15,7 +15,8 @@
};
use core::{
mem,
- ptr::NonNull, //
+ ptr::NonNull,
+ sync::atomic::*, //
};
/// Driver use the GEM memory manager. This should be set for all modern drivers.
@@ -127,7 +128,18 @@ pub trait Driver {
pub struct Registration<T: Driver>(ARef<drm::Device<T>>);
impl<T: Driver> Registration<T> {
- fn new(drm: drm::UnregisteredDevice<T>, flags: usize) -> Result<Self> {
+ fn new(
+ drm: drm::UnregisteredDevice<T>,
+ data: impl PinInit<T::Data, Error>,
+ flags: usize,
+ ) -> Result<Self> {
+ // SAFETY:
+ // - `raw_data` is a valid pointer to uninitialized memory.
+ // - `raw_data` will not move until it is dropped.
+ unsafe { data.__pinned_init(drm.0.data.get().cast()) }?;
+
+ drm.data_is_init.store(true, Ordering::Relaxed);
+
// SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Device`.
to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags) })?;
@@ -150,6 +162,7 @@ fn new(drm: drm::UnregisteredDevice<T>, flags: usize) -> Result<Self> {
pub fn new_foreign_owned<'a>(
drm: drm::UnregisteredDevice<T>,
dev: &'a device::Device<device::Bound>,
+ data: impl PinInit<T::Data, Error>,
flags: usize,
) -> Result<&'a drm::Device<T>>
where
@@ -159,7 +172,7 @@ pub fn new_foreign_owned<'a>(
return Err(EINVAL);
}
- let reg = Registration::<T>::new(drm, flags)?;
+ let reg = Registration::<T>::new(drm, data, flags)?;
let drm = NonNull::from(reg.device());
devres::register(dev, reg, GFP_KERNEL)?;
--
2.53.0
next prev parent reply other threads:[~2026-03-17 21:32 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-17 21:28 [PATCH v5 0/4] Introduce DeviceContext Lyude Paul
2026-03-17 21:28 ` [PATCH v5 1/4] rust/drm: " Lyude Paul
2026-03-17 21:28 ` Lyude Paul [this message]
2026-03-17 21:28 ` [PATCH v5 3/4] rust/drm/gem: Add DriverAllocImpl type alias Lyude Paul
2026-03-17 21:28 ` [PATCH v5 4/4] rust/drm/gem: Use DeviceContext with GEM objects Lyude Paul
-- strict thread matches above, loose matches on Subject: below --
2026-01-31 0:13 [PATCH v5 0/4] Introduce DeviceContext Lyude Paul
2026-01-31 0:13 ` [PATCH v5 2/4] rust/drm: Don't setup private driver data until registration Lyude Paul
2026-02-04 18:28 ` Daniel Almeida
2026-02-04 20:56 ` lyude
2026-02-06 13:46 ` Daniel Almeida
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260317213003.606088-3-lyude@redhat.com \
--to=lyude@redhat.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=gary@garyguo.net \
--cc=lina+kernel@asahilina.net \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=nouveau@lists.freedesktop.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=shankari.ak0208@gmail.com \
--cc=simona@ffwll.ch \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox