diff options
| author | Danilo Krummrich <dakr@kernel.org> | 2026-06-28 16:53:33 +0200 |
|---|---|---|
| committer | Danilo Krummrich <dakr@kernel.org> | 2026-07-12 15:59:28 +0200 |
| commit | 478da53e5b682f0c39a4c6c3eeb09c0131342e8a (patch) | |
| tree | e03dbefe6d3fa8987b525abd3075e77f63714b18 /rust/kernel/drm | |
| parent | 2455d5f2d5e879b42fe6ab3207b7e3f4b9c78383 (diff) | |
| download | linux-next-478da53e5b682f0c39a4c6c3eeb09c0131342e8a.tar.gz linux-next-478da53e5b682f0c39a4c6c3eeb09c0131342e8a.zip | |
rust: drm: Wrap ioctl dispatch in RegistrationGuard
Make Ioctl handlers receive a &Device<T, Registered> reference, proving
at the type level that the device is registered and its parent bus
device is bound.
This is achieved by calling registration_guard() on the Device<T, Ioctl>
obtained in ioctl dispatch context. If the device has been unplugged,
the ioctl returns -ENODEV without calling the handler.
To resolve the driver type parameter T for type inference, which the
compiler cannot propagate through method resolution and associated-type
projections alone, a dead-code closure and a helper function are used as
a type-inference anchor.
Reviewed-by: Lyude Paul <lyude@redhat.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Tested-by: Deborah Brouwer <deborah.brouwer@collabora.com>
Link: https://patch.msgid.link/20260628145406.2107056-14-dakr@kernel.org
[ Use imperative mood in commit message; clarify __dev_ctx_cast() doc
comment to reflect Ioctl-to-Registered cast. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Diffstat (limited to 'rust/kernel/drm')
| -rw-r--r-- | rust/kernel/drm/ioctl.rs | 45 |
1 files changed, 42 insertions, 3 deletions
diff --git a/rust/kernel/drm/ioctl.rs b/rust/kernel/drm/ioctl.rs index 6f5a9877bdae..c70ad5e2e5a1 100644 --- a/rust/kernel/drm/ioctl.rs +++ b/rust/kernel/drm/ioctl.rs @@ -70,6 +70,18 @@ pub mod internal { pub use bindings::drm_device; pub use bindings::drm_file; pub use bindings::drm_ioctl_desc; + + /// Cast an [`Ioctl`] DRM device pointer to [`Registered`], preserving the driver type + /// parameter `T`. + /// + /// Used by [`declare_drm_ioctls!`] to anchor type inference. + #[doc(hidden)] + #[inline] + pub const fn __dev_ctx_cast<T: crate::drm::Driver>( + ptr: *const crate::drm::Device<T, crate::drm::Ioctl>, + ) -> *const crate::drm::Device<T, crate::drm::Registered> { + ptr.cast() + } } /// Declare the DRM ioctls for a driver. @@ -82,7 +94,7 @@ pub mod internal { /// `user_callback` should have the following prototype: /// /// ```ignore -/// fn foo(device: &kernel::drm::Device<Self>, +/// fn foo(device: &kernel::drm::Device<Self, kernel::drm::Registered>, /// data: &mut uapi::argument_type, /// file: &kernel::drm::File<Self::File>, /// ) -> Result<u32> @@ -131,17 +143,44 @@ macro_rules! declare_drm_ioctls { // - The DRM device must have been registered when we're called through // an IOCTL. // + // INVARIANT: The `Ioctl` context requires that the device has been + // registered via `drm_dev_register()` at some point; the DRM core + // guarantees this for ioctl dispatch callbacks. + // // FIXME: Currently there is nothing enforcing that the types of the // dev/file match the current driver these ioctls are being declared // for, and it's not clear how to enforce this within the type system. - let dev: &$crate::drm::device::Device<_, $crate::drm::Normal> = + let dev: &$crate::drm::device::Device<_, $crate::drm::Ioctl> = $crate::drm::device::Device::from_raw(raw_dev); + // Type-inference anchor: the closure is never called but ties `dev`'s + // type to `$func`'s first parameter, which the compiler cannot infer + // through method resolution and associated-type projections alone. + #[allow(unreachable_code)] + let _ = || { + let __ptr = $crate::drm::ioctl::internal::__dev_ctx_cast( + ::core::ptr::from_ref(dev), + ); + + $func( + // SAFETY: This closure is never executed; the dereference + // exists purely to unify the type parameter with `$func`. + // The pointer is valid regardless. + unsafe { &*__ptr }, + unreachable!(), + unreachable!(), + ) + }; + // Enforce that the handler accepts higher-ranked // lifetimes, preventing it from requiring 'static // references that could escape this scope. let _: for<'a> fn(&'a _, &'a mut _, &'a _) -> _ = $func; + let Some(guard) = dev.registration_guard() else { + return $crate::error::code::ENODEV.to_errno(); + }; + // SAFETY: The ioctl argument has size `_IOC_SIZE(cmd)`, which we // asserted above matches the size of this type, and all bit patterns of // UAPI structs must be valid. @@ -154,7 +193,7 @@ macro_rules! declare_drm_ioctls { // SAFETY: This is just the DRM file structure let file = unsafe { $crate::drm::File::from_raw(raw_file) }; - match $func(dev, data, file) { + match $func(&*guard, data, file) { Err(e) => e.to_errno(), Ok(i) => i.try_into() .unwrap_or($crate::error::code::ERANGE.to_errno()), |
