aboutsummaryrefslogtreecommitdiff
path: root/rust/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'rust/kernel')
-rw-r--r--rust/kernel/alloc/kvec.rs27
-rw-r--r--rust/kernel/dma.rs70
-rw-r--r--rust/kernel/drm/device.rs291
-rw-r--r--rust/kernel/drm/driver.rs113
-rw-r--r--rust/kernel/drm/gem/mod.rs103
-rw-r--r--rust/kernel/drm/gem/shmem.rs594
-rw-r--r--rust/kernel/drm/gpuvm/mod.rs36
-rw-r--r--rust/kernel/drm/gpuvm/sm_ops.rs4
-rw-r--r--rust/kernel/drm/gpuvm/va.rs8
-rw-r--r--rust/kernel/drm/gpuvm/vm_bo.rs9
-rw-r--r--rust/kernel/drm/ioctl.rs56
-rw-r--r--rust/kernel/drm/mod.rs4
-rw-r--r--rust/kernel/faux.rs81
-rw-r--r--rust/kernel/firmware.rs46
-rw-r--r--rust/kernel/iommu/pgtable.rs32
-rw-r--r--rust/kernel/pci.rs13
16 files changed, 1162 insertions, 325 deletions
diff --git a/rust/kernel/alloc/kvec.rs b/rust/kernel/alloc/kvec.rs
index f7af62835aa8..c7546b9da4fa 100644
--- a/rust/kernel/alloc/kvec.rs
+++ b/rust/kernel/alloc/kvec.rs
@@ -9,6 +9,7 @@ use super::{
Vmalloc,
VmallocPageIter, //
},
+ flags::__GFP_ZERO,
layout::ArrayLayout,
AllocError,
Allocator,
@@ -51,6 +52,8 @@ use core::{
}, //
};
+use pin_init::Zeroable;
+
mod errors;
pub use self::errors::{InsertError, PushError, RemoveError};
@@ -532,6 +535,30 @@ where
Ok(v)
}
+ /// Creates a new [`Vec`] with `n` zero-initialized elements.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// let v = KVec::<u32>::zeroed(20, GFP_KERNEL)?;
+ ///
+ /// assert!(v.iter().all(|&x| x == 0));
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn zeroed(n: usize, flags: Flags) -> Result<Self, AllocError>
+ where
+ T: Zeroable,
+ {
+ let mut v = Self::with_capacity(n, flags | __GFP_ZERO)?;
+
+ // SAFETY:
+ // - `n <= capacity - len`: `with_capacity(n)` guarantees capacity >= n, len is 0.
+ // - All elements in `[0, n)` are initialized: `__GFP_ZERO` zeroes the allocation,
+ // and `T: Zeroable` guarantees all-zeroes is a valid bit pattern.
+ unsafe { v.inc_len(n) };
+ Ok(v)
+ }
+
/// Creates a `Vec<T, A>` from a pointer, a length and a capacity using the allocator `A`.
///
/// # Examples
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 9a0bf745614c..2ce09f8e90c6 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -585,7 +585,7 @@ impl<T: AsBytes + FromBytes + KnownSize + ?Sized> From<CoherentBox<T>> for Coher
/// # Invariants
///
/// - For the lifetime of an instance of [`Coherent`], the `cpu_addr` is a valid pointer
-/// to an allocated region of coherent memory and `dma_handle` is the DMA address base of the
+/// to an allocated region of coherent memory and `dma_addr` is the DMA address base of the
/// region.
/// - The size in bytes of the allocation is equal to size information via pointer.
// TODO
@@ -602,7 +602,7 @@ impl<T: AsBytes + FromBytes + KnownSize + ?Sized> From<CoherentBox<T>> for Coher
// entire `Coherent` including the allocated memory itself.
pub struct Coherent<T: KnownSize + ?Sized> {
dev: ARef<device::Device>,
- dma_handle: DmaAddress,
+ dma_addr: DmaAddress,
cpu_addr: NonNull<T>,
dma_attrs: Attrs,
}
@@ -627,11 +627,10 @@ impl<T: KnownSize + ?Sized> Coherent<T> {
self.cpu_addr.as_ptr()
}
- /// Returns a DMA handle which may be given to the device as the DMA address base of
- /// the region.
+ /// Returns a DMA address which may be given to the device as the base of the region.
#[inline]
- pub fn dma_handle(&self) -> DmaAddress {
- self.dma_handle
+ pub fn dma_address(&self) -> DmaAddress {
+ self.dma_addr
}
/// Returns a reference to the data in the region.
@@ -678,13 +677,13 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
);
}
- let mut dma_handle = 0;
+ let mut dma_addr = 0;
// SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`.
let addr = unsafe {
bindings::dma_alloc_attrs(
dev.as_raw(),
core::mem::size_of::<T>(),
- &mut dma_handle,
+ &mut dma_addr,
gfp_flags.as_raw(),
dma_attrs.as_raw(),
)
@@ -696,7 +695,7 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
// - We also hold a refcounted reference to the device.
Ok(Self {
dev: dev.into(),
- dma_handle,
+ dma_addr,
cpu_addr,
dma_attrs,
})
@@ -795,13 +794,13 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
}
let size = core::mem::size_of::<T>().checked_mul(len).ok_or(ENOMEM)?;
- let mut dma_handle = 0;
+ let mut dma_addr = 0;
// SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`.
let addr = unsafe {
bindings::dma_alloc_attrs(
dev.as_raw(),
size,
- &mut dma_handle,
+ &mut dma_addr,
gfp_flags.as_raw(),
dma_attrs.as_raw(),
)
@@ -813,7 +812,7 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
// - We also hold a refcounted reference to the device.
Ok(Coherent {
dev: dev.into(),
- dma_handle,
+ dma_addr,
cpu_addr,
dma_attrs,
})
@@ -927,14 +926,14 @@ impl<T: KnownSize + ?Sized> Drop for Coherent<T> {
fn drop(&mut self) {
let size = T::size(self.cpu_addr.as_ptr());
// SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`.
- // The cpu address, and the dma handle are valid due to the type invariants on
+ // The cpu address, and the dma address are valid due to the type invariants on
// `Coherent`.
unsafe {
bindings::dma_free_attrs(
self.dev.as_raw(),
size,
self.cpu_addr.as_ptr().cast(),
- self.dma_handle,
+ self.dma_addr,
self.dma_attrs.as_raw(),
)
}
@@ -993,13 +992,13 @@ impl<T: KnownSize + AsBytes + ?Sized> debugfs::BinaryWriter for Coherent<T> {
///
/// - `cpu_handle` holds the opaque handle returned by `dma_alloc_attrs` with
/// `DMA_ATTR_NO_KERNEL_MAPPING` set, and is only valid for passing back to `dma_free_attrs`.
-/// - `dma_handle` is the corresponding bus address for device DMA.
+/// - `dma_addr` is the corresponding bus address for device DMA.
/// - `size` is the allocation size in bytes as passed to `dma_alloc_attrs`.
/// - `dma_attrs` contains the attributes used for the allocation, always including
/// `DMA_ATTR_NO_KERNEL_MAPPING`.
pub struct CoherentHandle {
dev: ARef<device::Device>,
- dma_handle: DmaAddress,
+ dma_addr: DmaAddress,
cpu_handle: NonNull<c_void>,
size: usize,
dma_attrs: Attrs,
@@ -1023,13 +1022,13 @@ impl CoherentHandle {
}
let dma_attrs = dma_attrs | Attrs(bindings::DMA_ATTR_NO_KERNEL_MAPPING);
- let mut dma_handle = 0;
+ let mut dma_addr = 0;
// SAFETY: `dev.as_raw()` is valid by the type invariant on `device::Device`.
let cpu_handle = unsafe {
bindings::dma_alloc_attrs(
dev.as_raw(),
size,
- &mut dma_handle,
+ &mut dma_addr,
gfp_flags.as_raw(),
dma_attrs.as_raw(),
)
@@ -1038,11 +1037,11 @@ impl CoherentHandle {
let cpu_handle = NonNull::new(cpu_handle).ok_or(ENOMEM)?;
// INVARIANT: `cpu_handle` is the opaque handle from a successful `dma_alloc_attrs` call
- // with `DMA_ATTR_NO_KERNEL_MAPPING`, `dma_handle` is the corresponding DMA address,
+ // with `DMA_ATTR_NO_KERNEL_MAPPING`, `dma_addr` is the corresponding DMA address,
// and we hold a refcounted reference to the device.
Ok(Self {
dev: dev.into(),
- dma_handle,
+ dma_addr,
cpu_handle,
size,
dma_attrs,
@@ -1059,12 +1058,12 @@ impl CoherentHandle {
Self::alloc_with_attrs(dev, size, gfp_flags, Attrs(0))
}
- /// Returns the DMA handle for this allocation.
+ /// Returns the DMA address for this allocation.
///
/// This address can be programmed into device hardware for DMA access.
#[inline]
- pub fn dma_handle(&self) -> DmaAddress {
- self.dma_handle
+ pub fn dma_address(&self) -> DmaAddress {
+ self.dma_addr
}
/// Returns the size in bytes of this allocation.
@@ -1083,28 +1082,29 @@ impl Drop for CoherentHandle {
self.dev.as_raw(),
self.size,
self.cpu_handle.as_ptr(),
- self.dma_handle,
+ self.dma_addr,
self.dma_attrs.as_raw(),
)
}
}
}
-// SAFETY: `CoherentHandle` only holds a device reference, a DMA handle, an opaque CPU handle,
+// SAFETY: `CoherentHandle` only holds a device reference, a DMA address, an opaque CPU handle,
// and a size. None of these are tied to a specific thread.
unsafe impl Send for CoherentHandle {}
// SAFETY: `CoherentHandle` provides no CPU access to the underlying allocation. The only
-// operations on `&CoherentHandle` are reading the DMA handle and size, both of which are
+// operations on `&CoherentHandle` are reading the DMA address and size, both of which are
// plain `Copy` values.
unsafe impl Sync for CoherentHandle {}
/// View type for `Coherent`.
///
-/// This is same as [`SysMem`] but with additional information that allows handing out a DMA handle.
+/// This is same as [`SysMem`] but with additional information that allows handing out a DMA
+/// address.
pub struct CoherentView<'a, T: ?Sized> {
cpu_addr: SysMem<'a, T>,
- dma_handle: DmaAddress,
+ dma_addr: DmaAddress,
}
impl<T: ?Sized> Copy for CoherentView<'_, T> {}
@@ -1116,16 +1116,16 @@ impl<T: ?Sized> Clone for CoherentView<'_, T> {
}
impl<'a, T: ?Sized> CoherentView<'a, T> {
- /// Erase the DMA handle information and obtain a [`SysMem`] view of the same memory region.
+ /// Erase the DMA address information and obtain a [`SysMem`] view of the same memory region.
#[inline]
pub fn as_sys_mem(self) -> SysMem<'a, T> {
self.cpu_addr
}
- /// Returns a DMA handle which may be given to the device as the DMA address base of the region.
+ /// Returns the DMA address which may be given to the device as base of the region.
#[inline]
- pub fn dma_handle(self) -> DmaAddress {
- self.dma_handle
+ pub fn dma_address(self) -> DmaAddress {
+ self.dma_addr
}
/// Returns a reference to the data in the region.
@@ -1178,9 +1178,9 @@ impl IoBackend for CoherentIoBackend {
) -> Self::View<'a, U> {
let offset = ptr.addr() - view.cpu_addr.as_ptr().addr();
// CAST: The offset DMA address can never overflow.
- let dma_handle = view.dma_handle + offset as DmaAddress;
+ let dma_addr = view.dma_addr + offset as DmaAddress;
CoherentView {
- dma_handle,
+ dma_addr,
// SAFETY: Per safety requirement.
cpu_addr: unsafe { SysMemBackend::project_view(view.cpu_addr, ptr) },
}
@@ -1245,7 +1245,7 @@ impl<'a, T: ?Sized + KnownSize> IoBase<'a> for &'a Coherent<T> {
CoherentView {
// SAFETY: `cpu_addr` is valid and aligned kernel accessible memory.
cpu_addr: unsafe { SysMem::new(self.cpu_addr.as_ptr()) },
- dma_handle: self.dma_handle,
+ dma_addr: self.dma_addr,
}
}
}
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs
index 81f9f7e59817..6b88ade28e24 100644
--- a/rust/kernel/drm/device.rs
+++ b/rust/kernel/drm/device.rs
@@ -32,6 +32,7 @@ use crate::{
};
use core::{
alloc::Layout,
+ cell::UnsafeCell,
marker::PhantomData,
mem,
ops::Deref,
@@ -74,66 +75,59 @@ macro_rules! drm_legacy_fields {
/// A trait implemented by all possible contexts a [`Device`] can be used in.
///
-/// Setting up a new [`Device`] is a multi-stage process. Each step of the process that a user
-/// interacts with in Rust has a respective [`DeviceContext`] typestate. For example,
-/// `Device<T, Registered>` would be a [`Device`] that reached the [`Registered`] [`DeviceContext`].
+/// A [`Device`] can be in one of the following contexts:
///
-/// Each stage of this process is described below:
+/// - [`Normal`]: The general-purpose, reference-counted context. A [`Device`] in this context may
+/// or may not be registered with userspace.
+/// - [`Ioctl`]: The device has been registered with userspace at some point; used in ioctl
+/// dispatch context.
+/// - [`Registered`]: The device is currently registered with userspace and the parent bus device
+/// is bound.
///
-/// ```text
-/// 1 2 3
-/// +--------------+ +------------------+ +-----------------------+
-/// |Device created| → |Device initialized| → |Registered w/ userspace|
-/// +--------------+ +------------------+ +-----------------------+
-/// (Uninit) (Registered)
-/// ```
-///
-/// 1. The [`Device`] is in the [`Uninit`] context and is not guaranteed to be initialized or
-/// registered with userspace. Only a limited subset of DRM core functionality is available.
-/// 2. The [`Device`] is guaranteed to be fully initialized, but is not guaranteed to be registered
-/// with userspace. All DRM core functionality which doesn't interact with userspace is
-/// available. We currently don't have a context for representing this.
-/// 3. The [`Device`] is guaranteed to be fully initialized, and is guaranteed to have been
-/// registered with userspace at some point - thus putting it in the [`Registered`] context.
+/// Both `Device<T, Ioctl>` and `Device<T, Registered>` dereference to `Device<T>` ([`Normal`]),
+/// so any method available on a [`Normal`] device is also available in the other contexts.
+pub trait DeviceContext: Sealed + Send + Sync + 'static {}
+
+/// The general-purpose, reference-counted [`DeviceContext`].
///
-/// An important caveat of [`DeviceContext`] which must be kept in mind: when used as a typestate
-/// for a reference type, it can only guarantee that a [`Device`] reached a particular stage in the
-/// initialization process _at the time the reference was taken_. No guarantee is made in regards to
-/// what stage of the process the [`Device`] is currently in. This means for instance that a
-/// `&Device<T, Uninit>` may actually be registered with userspace, it just wasn't known to be
-/// registered at the time the reference was taken.
-pub trait DeviceContext: Sealed + Send + Sync {}
-
-/// The [`DeviceContext`] of a [`Device`] that was registered with userspace at some point.
+/// A [`Device`] in this context may or may not be registered with userspace. This context is used
+/// for reference-counted device handles and during device setup via [`UnregisteredDevice`].
///
-/// 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.
+/// [`AlwaysRefCounted`] is only implemented for `Device<T, Normal>`, making this the required
+/// context for [`ARef`]-based device handles.
+pub struct Normal;
+
+impl Sealed for Normal {}
+impl DeviceContext for Normal {}
+
+/// The [`DeviceContext`] of a [`Device`] that is currently registered with userspace.
///
-/// 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 {}
impl DeviceContext for Registered {}
-/// The [`DeviceContext`] of a [`Device`] that may be unregistered and partly uninitialized.
+/// The [`DeviceContext`] of a [`Device`] that has been registered with userspace previously.
///
-/// A [`Device`] in this context is only guaranteed to be partly initialized, and may or may not
-/// be registered with userspace. Thus operations which depend on the [`Device`] being fully
-/// initialized, or which depend on the [`Device`] being registered with userspace are not
-/// available through this [`DeviceContext`].
+/// A [`Device`] in this context has been registered at some point, but may be concurrently
+/// unregistering or already unregistered. `drm_dev_enter()` can guard against this, ensuring the
+/// device remains registered for the duration of the critical section.
///
-/// A [`Device`] in this context can be used to create a
-/// [`Registration`](drm::driver::Registration).
-pub struct Uninit;
+/// # Invariants
+///
+/// A [`Device`] in this context has been registered with userspace via `drm_dev_register()` at
+/// some point.
+pub struct Ioctl;
-impl Sealed for Uninit {}
-impl DeviceContext for Uninit {}
+impl Sealed for Ioctl {}
+impl DeviceContext for Ioctl {}
/// A [`Device`] which is known at compile-time to be unregistered with userspace.
///
@@ -147,10 +141,10 @@ 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>(ARef<Device<T, Normal>>, NotThreadSafe);
impl<T: drm::Driver> Deref for UnregisteredDevice<T> {
- type Target = Device<T, Uninit>;
+ type Target = Device<T, Normal>;
fn deref(&self) -> &Self::Target {
&self.0
@@ -178,15 +172,13 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
master_drop: None,
debugfs_init: None,
- // Ignore the Uninit DeviceContext below. It is only provided because it is required by the
- // compiler, and it is not actually used by these functions.
- gem_create_object: T::Object::<Uninit>::ALLOC_OPS.gem_create_object,
- prime_handle_to_fd: T::Object::<Uninit>::ALLOC_OPS.prime_handle_to_fd,
- prime_fd_to_handle: T::Object::<Uninit>::ALLOC_OPS.prime_fd_to_handle,
- gem_prime_import: T::Object::<Uninit>::ALLOC_OPS.gem_prime_import,
- gem_prime_import_sg_table: T::Object::<Uninit>::ALLOC_OPS.gem_prime_import_sg_table,
- dumb_create: T::Object::<Uninit>::ALLOC_OPS.dumb_create,
- dumb_map_offset: T::Object::<Uninit>::ALLOC_OPS.dumb_map_offset,
+ gem_create_object: T::Object::ALLOC_OPS.gem_create_object,
+ prime_handle_to_fd: T::Object::ALLOC_OPS.prime_handle_to_fd,
+ prime_fd_to_handle: T::Object::ALLOC_OPS.prime_fd_to_handle,
+ gem_prime_import: T::Object::ALLOC_OPS.gem_prime_import,
+ gem_prime_import_sg_table: T::Object::ALLOC_OPS.gem_prime_import_sg_table,
+ dumb_create: T::Object::ALLOC_OPS.dumb_create,
+ dumb_map_offset: T::Object::ALLOC_OPS.dumb_map_offset,
show_fdinfo: None,
fbdev_probe: None,
@@ -209,10 +201,13 @@ 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: &T::ParentDevice<device::Bound>,
+ data: impl PinInit<T::Data, Error>,
+ ) -> 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>>());
+ let layout = Kmalloc::aligned_layout(Layout::new::<Device<T, Normal>>());
// Use a temporary vtable without a `release` callback until `data` is initialized, so
// init failure can release the DRM device without dropping uninitialized fields.
@@ -224,12 +219,12 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
// SAFETY:
// - `alloc_vtable` reference remains valid until no longer used,
// - `dev` is valid by its type invarants,
- let raw_drm: *mut Device<T, Uninit> = unsafe {
+ let raw_drm: *mut Device<T, Normal> = unsafe {
bindings::__drm_dev_alloc(
- dev.as_raw(),
+ dev.as_ref().as_raw(),
&alloc_vtable,
layout.size(),
- mem::offset_of!(Device<T, Uninit>, dev),
+ mem::offset_of!(Device<T, Normal>, dev),
)
}
.cast();
@@ -254,6 +249,9 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
// SAFETY: `drm_dev` is still private to this function.
unsafe { (*drm_dev).driver = const { &Self::VTABLE } };
+ // SAFETY: `raw_drm` is valid; no concurrent access before registration.
+ unsafe { (*raw_drm.as_ptr()).registration_data = UnsafeCell::new(NonNull::dangling()) };
+
// 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`.
@@ -265,16 +263,8 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
/// A typed DRM device with a specific [`drm::Driver`] implementation and [`DeviceContext`].
///
-/// Since DRM devices can be used before being fully initialized and registered with userspace, `C`
-/// represents the furthest [`DeviceContext`] we can guarantee that this [`Device`] has reached.
-///
-/// Keep in mind: this means that an unregistered device can still have the registration state
-/// [`Registered`] as long as it was registered with userspace once in the past, and that the
-/// behavior of such a device is still well-defined. Additionally, a device with the registration
-/// state [`Uninit`] simply does not have a guaranteed registration state at compile time, and could
-/// be either registered or unregistered. Since there is no way to guarantee a long-lived reference
-/// to an unregistered device would remain unregistered, we do not provide a [`DeviceContext`] for
-/// this.
+/// 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
///
@@ -282,9 +272,10 @@ impl<T: drm::Driver> UnregisteredDevice<T> {
/// * The data layout of `Self` remains the same across all implementations of `C`.
/// * Any invariants for `C` also apply.
#[repr(C)]
-pub struct Device<T: drm::Driver, C: DeviceContext = Registered> {
+pub struct Device<T: drm::Driver, C: DeviceContext = Normal> {
dev: Opaque<bindings::drm_device>,
data: T::Data,
+ pub(super) registration_data: UnsafeCell<NonNull<T::RegistrationData<'static>>>,
_ctx: PhantomData<C>,
}
@@ -353,7 +344,111 @@ impl<T: drm::Driver, C: DeviceContext> Device<T, C> {
}
}
-impl<T: drm::Driver, C: DeviceContext> Deref for Device<T, C> {
+impl<T: drm::Driver> Device<T, Ioctl> {
+ /// 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) } {
+ // INVARIANT:
+ // - `idx` is the SRCU index from the successful `drm_dev_enter()` above.
+ // - The parent bus device is bound: `drm_dev_enter()` succeeded, meaning
+ // `drm_dev_unplug()` has not completed; since it is only called from
+ // `Registration::drop()` during parent unbind, the parent is still bound.
+ Some(RegistrationGuard {
+ // SAFETY: See INVARIANT above; the `Registered` context invariant holds.
+ dev: unsafe { self.assume_ctx() },
+ idx,
+ _not_send: NotThreadSafe,
+ })
+ } 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, Registered>,
+ idx: i32,
+ _not_send: NotThreadSafe,
+}
+
+impl<T: drm::Driver> Device<T, Registered> {
+ /// Returns a reference to the registration data with lifetime shortened from `'static`.
+ ///
+ /// # Safety
+ ///
+ /// The returned reference must not be exposed to code that can choose a concrete lifetime for
+ /// it, as that would be unsound for types that are invariant over their lifetime parameter
+ /// (e.g. it must be passed through an HRTB-bounded closure).
+ #[inline]
+ unsafe fn registration_data_unchecked(&self) -> &T::RegistrationData<'_> {
+ // SAFETY:
+ // - `Registered` guarantees the parent bus device is bound, hence the pointer is valid.
+ // - The pointer cast from `Of<'static>` to `Of<'_>` is layout-compatible since lifetimes
+ // are erased at runtime.
+ // - Caller guarantees the reference is only used behind an HRTB, making the lifetime
+ // shortening sound regardless of variance.
+ unsafe { (*self.registration_data.get()).cast::<_>().as_ref() }
+ }
+
+ /// Access the registration data through a closure, with the lifetime tied to the closure
+ /// scope.
+ ///
+ /// The data is owned by [`Registration`](drm::Registration) and is guaranteed to remain valid
+ /// as long as the device is registered, since [`Registration`](drm::Registration)'s `drop`
+ /// calls `drm_dev_unplug()` which waits for all `drm_dev_enter()` critical sections to
+ /// complete.
+ #[inline]
+ pub fn registration_data_with<R, F>(&self, f: F) -> R
+ where
+ F: for<'a> FnOnce(&'a T::RegistrationData<'a>) -> R,
+ {
+ // SAFETY: `Registered` guarantees the device is registered and the parent bus device is
+ // bound. The closure's HRTB `for<'a>` prevents the caller from smuggling in references
+ // with a concrete short lifetime, satisfying the lifetime requirement of
+ // `registration_data_unchecked`.
+ f(unsafe { self.registration_data_unchecked() })
+ }
+}
+
+impl<T: drm::Driver> Deref for RegistrationGuard<'_, T> {
+ type Target = Device<T, Registered>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ self.dev
+ }
+}
+
+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;
fn deref(&self) -> &Self::Target {
@@ -361,9 +456,31 @@ impl<T: drm::Driver, C: DeviceContext> Deref for Device<T, C> {
}
}
+impl<T: drm::Driver> Deref for Device<T, Registered> {
+ type Target = Device<T>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The caller holds a `Device<T, Registered>`, which guarantees all invariants
+ // of the weaker `Normal` context.
+ unsafe { self.assume_ctx() }
+ }
+}
+
+impl<T: drm::Driver> Deref for Device<T, Ioctl> {
+ type Target = Device<T>;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: The caller holds a `Device<T, Ioctl>`, which guarantees all invariants
+ // of the weaker `Normal` context.
+ unsafe { self.assume_ctx() }
+ }
+}
+
// SAFETY: DRM device objects are always reference counted and the get/put functions
// satisfy the requirements.
-unsafe impl<T: drm::Driver, C: DeviceContext> AlwaysRefCounted for Device<T, C> {
+unsafe impl<T: drm::Driver> AlwaysRefCounted for Device<T> {
fn inc_ref(&self) {
// SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
unsafe { bindings::drm_dev_get(self.as_raw()) };
@@ -378,11 +495,29 @@ unsafe impl<T: drm::Driver, C: DeviceContext> AlwaysRefCounted for Device<T, C>
}
}
-impl<T: drm::Driver, C: DeviceContext> AsRef<device::Device> for Device<T, C> {
- fn as_ref(&self) -> &device::Device {
+impl<T: drm::Driver> AsRef<T::ParentDevice<device::Normal>> for Device<T> {
+ fn as_ref(&self) -> &T::ParentDevice<device::Normal> {
// SAFETY: `bindings::drm_device::dev` is valid as long as the DRM device itself is valid,
// which is guaranteed by the type invariant.
- unsafe { device::Device::from_raw((*self.as_raw()).dev) }
+ let dev = unsafe { device::Device::from_raw((*self.as_raw()).dev) };
+
+ // SAFETY: The DRM device was constructed in `UnregisteredDevice::new()` with a parent
+ // device of type `T::ParentDevice`, hence `dev` is contained in a `T::ParentDevice`.
+ unsafe { device::AsBusDevice::from_device(dev) }
+ }
+}
+
+impl<T: drm::Driver> AsRef<T::ParentDevice<device::Bound>> for Device<T, Registered> {
+ #[inline]
+ fn as_ref(&self) -> &T::ParentDevice<device::Bound> {
+ let dev = (**self).as_ref().as_ref();
+
+ // SAFETY: A `Device<T, Registered>` guarantees that the parent device is bound.
+ let dev = unsafe { dev.as_bound() };
+
+ // SAFETY: The DRM device was constructed in `UnregisteredDevice::new()` with a parent
+ // device of type `T::ParentDevice`, hence `dev` is contained in a `T::ParentDevice`.
+ unsafe { device::AsBusDevice::from_device(dev) }
}
}
@@ -393,12 +528,10 @@ unsafe impl<T: drm::Driver, C: DeviceContext> Send for Device<T, C> {}
// by the synchronization in `struct drm_device`.
unsafe impl<T: drm::Driver, C: DeviceContext> Sync for Device<T, C> {}
-impl<T, C, const ID: u64> WorkItem<ID> for Device<T, C>
+impl<T: drm::Driver, const ID: u64> WorkItem<ID> for Device<T>
where
- T: drm::Driver,
T::Data: WorkItem<ID, Pointer = ARef<Self>>,
T::Data: HasWork<Self, ID>,
- C: DeviceContext,
{
type Pointer = ARef<Self>;
diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs
index 25f7e233884d..74f6ed690d8b 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,12 +106,23 @@ 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<Ctx: drm::DeviceContext>: AllocImpl;
+ type Object: AllocImpl;
/// The type used to represent a DRM File (client)
type File: drm::file::DriverFile;
+ /// The bus device type of the parent device that the DRM device is associated with.
+ type ParentDevice<Ctx: device::DeviceContext>: device::AsBusDevice<Ctx>;
+
/// Driver metadata
const INFO: DriverInfo;
@@ -125,7 +132,7 @@ pub trait Driver {
/// Sets the `DRIVER_RENDER` feature for this driver.
///
/// When enabled, the driver exposes `/dev/dri/renderDXX` render nodes to
- /// userspace. The render node is an alternate low-priviledge way to access
+ /// userspace. The render node is an alternate low-privilege way to access
/// the driver, which is enforced on a per-ioctl level. Userspace processes
/// that open the render node can only invoke ioctls explicitly listed as
/// usable from the render node (i.e. marked DRM_RENDER_ALLOW), whereas
@@ -136,68 +143,84 @@ 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