diff options
Diffstat (limited to 'rust/kernel')
| -rw-r--r-- | rust/kernel/alloc/kvec.rs | 27 | ||||
| -rw-r--r-- | rust/kernel/dma.rs | 70 | ||||
| -rw-r--r-- | rust/kernel/drm/device.rs | 291 | ||||
| -rw-r--r-- | rust/kernel/drm/driver.rs | 113 | ||||
| -rw-r--r-- | rust/kernel/drm/gem/mod.rs | 103 | ||||
| -rw-r--r-- | rust/kernel/drm/gem/shmem.rs | 594 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/mod.rs | 36 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/sm_ops.rs | 4 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/va.rs | 8 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/vm_bo.rs | 9 | ||||
| -rw-r--r-- | rust/kernel/drm/ioctl.rs | 56 | ||||
| -rw-r--r-- | rust/kernel/drm/mod.rs | 4 | ||||
| -rw-r--r-- | rust/kernel/faux.rs | 81 | ||||
| -rw-r--r-- | rust/kernel/firmware.rs | 46 | ||||
| -rw-r--r-- | rust/kernel/iommu/pgtable.rs | 32 | ||||
| -rw-r--r-- | rust/kernel/pci.rs | 13 |
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 |
