summaryrefslogtreecommitdiff
path: root/rust/kernel/drm/driver.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel/drm/driver.rs')
-rw-r--r--rust/kernel/drm/driver.rs95
1 files changed, 54 insertions, 41 deletions
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 9ba2eba84191..08b2a318cf02 100644
--- a/rust/kernel/drm/driver.rs
+++ b/rust/kernel/drm/driver.rs
@@ -7,16 +7,12 @@
use crate::{
bindings,
device,
- devres,
drm,
error::to_result,
prelude::*,
sync::aref::ARef, //
};
-use core::{
- mem,
- ptr::NonNull, //
-};
+use core::ptr::NonNull;
/// Driver use the GEM memory manager. This should be set for all modern drivers.
pub(crate) const FEAT_GEM: u32 = bindings::drm_driver_feature_DRIVER_GEM;
@@ -110,6 +106,14 @@ pub trait Driver {
/// Context data associated with the DRM driver
type Data: Sync + Send;
+ /// Data owned by the [`Registration`] and accessible within a
+ /// [`RegistrationGuard`](drm::RegistrationGuard) critical section via
+ /// [`Device::registration_data_with()`](drm::Device::registration_data_with).
+ ///
+ /// The lifetime parameter is tied to the [`Registration`] scope, which is enclosed in the
+ /// parent bus device binding scope but may be shorter.
+ type RegistrationData<'a>: Send + Sync + 'a;
+
/// The type used to manage memory for this driver.
type Object: AllocImpl;
@@ -139,66 +143,72 @@ pub trait Driver {
/// 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> {
- fn new(drm: drm::UnregisteredDevice<T>, flags: usize) -> Result<Self> {
- // SAFETY: `drm.as_raw()` is valid by the invariants of `drm::Device`.
- to_result(unsafe { bindings::drm_dev_register(drm.as_raw(), flags) })?;
-
- // SAFETY: We just called `drm_dev_register` above
- let new = NonNull::from(unsafe { drm.assume_ctx() });
-
- // Leak the ARef from UnregisteredDevice in preparation for transferring its ownership.
- mem::forget(drm);
-
- // SAFETY: `drm`'s `Drop` constructor was never called, ensuring that there remains at least
- // one reference to the device - which we take ownership over here.
- let new = unsafe { ARef::from_raw(new) };
-
- Ok(Self(new))
- }
+pub struct Registration<'a, T: Driver> {
+ drm: ARef<drm::Device<T>>,
+ _reg_data: Pin<KBox<T::RegistrationData<'a>>>,
+}
- /// Registers a new [`UnregisteredDevice`](drm::UnregisteredDevice) with userspace.
+impl<'a, T: Driver> Registration<'a, T> {
+ /// Register a new [`UnregisteredDevice`](drm::UnregisteredDevice) with userspace.
///
- /// Ownership of the [`Registration`] object is passed to [`devres::register`].
- pub fn new_foreign_owned<'a>(
- drm: drm::UnregisteredDevice<T>,
+ /// # Safety
+ ///
+ /// The caller must not `mem::forget()` the returned [`Registration`] or otherwise prevent its
+ /// [`Drop`] implementation from running, since the registration data may contain borrowed
+ /// references that become invalid after `'a` ends.
+ pub unsafe fn new<E>(
dev: &'a device::Device<device::Bound>,
+ drm: drm::UnregisteredDevice<T>,
+ reg_data: impl PinInit<T::RegistrationData<'a>, E>,
flags: usize,
- ) -> Result<&'a drm::Device<T>>
+ ) -> Result<Self>
where
- T: 'static,
+ Error: From<E>,
{
let parent = drm.as_ref();
if parent.as_ref().as_raw() != dev.as_raw() {
return Err(EINVAL);
}
- let reg = Registration::<T>::new(drm, flags)?;
- let drm = NonNull::from(reg.device());
+ let reg_data: Pin<KBox<T::RegistrationData<'a>>> = KBox::pin_init(reg_data, GFP_KERNEL)?;
+
+ // Store the registration data pointer in the device before registration, so that it is
+ // visible once ioctls can be called.
+ let ptr: NonNull<T::RegistrationData<'static>> =
+ NonNull::from(Pin::get_ref(reg_data.as_ref())).cast();
- devres::register(dev, reg, GFP_KERNEL)?;
+ // SAFETY: No concurrent access; the device is not yet registered.
+ unsafe { *drm.registration_data.get() = ptr };
+
+ // SAFETY: `drm` is a valid, initialized but not yet registered DRM device.
+ let ret = unsafe { bindings::drm_dev_register(drm.as_raw(), flags) };
+ if let Err(e) = to_result(ret) {
+ // SAFETY: `drm_dev_register()` synchronizes SRCU on failure, so no concurrent
+ // access to `registration_data` is possible at this point.
+ unsafe { *drm.registration_data.get() = NonNull::dangling() };
+ return Err(e);
+ }
- // SAFETY: Since `reg` was passed to devres::register(), the device now owns the lifetime
- // of the DRM registration - ensuring that this references lives for at least as long as 'a.
- Ok(unsafe { drm.as_ref() })
+ Ok(Self {
+ drm: (&*drm).into(),
+ _reg_data: reg_data,
+ })
}
/// Returns a reference to the `Device` instance for this registration.
pub fn device(&self) -> &drm::Device<T> {
- &self.0
+ &self.drm
}
}
// 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> {}
+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> {}
+unsafe impl<T: Driver> Send for Registration<'_, T> {}
-impl<T: Driver> Drop 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
@@ -208,6 +218,9 @@ impl<T: Driver> Drop for Registration<T> {
//
// SAFETY: Safe by the invariant of `ARef<drm::Device<T>>`. The existence of this
// `Registration` also guarantees that this `drm::Device` is actually registered.
- unsafe { bindings::drm_dev_unplug(self.0.as_raw()) };
+ unsafe { bindings::drm_dev_unplug(self.drm.as_raw()) };
+ // After drm_dev_unplug(), the SRCU barrier guarantees that all RegistrationGuard critical
+ // sections have completed, so no one holds a reference to reg_data anymore.
+ // reg_data is dropped here automatically.
}
}