diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-17 10:21:00 +0100 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-06-17 10:21:00 +0100 |
| commit | 4b99990cdf9560e8a071640baf19f312e6ae02f4 (patch) | |
| tree | ba3c58e860666130caf5ae3bf386b6dbfbe59b04 /rust/kernel | |
| parent | 9c87e61e3c5797277407ba5eae4eac8a52be3fa3 (diff) | |
| parent | 52d4ab1ca790a668cc8f2c27017138b1c467168c (diff) | |
Merge tag 'drm-next-2026-06-17' of https://gitlab.freedesktop.org/drm/kernel
Pull drm updates from Dave Airlie:
"Highlights:
- xe: add initial CRI platform support
- amdgpu: initial HDMI 2.1 FRL support
- rust: add some new type concepts for device lifetimes
- scheduler: moves to a fair algorithm and lots of cleanups
But it's mostly the usual mountain of changes across the board.
core:
- add docbook for DRM_IOCTL_SYNCOBJ_EVENTFD
- change signature of drm_connector_attach_hdr_output_metadata_property
- dedup counter and timestamp retrieval in vblank code
- parse AMD VSDB v3 in CTA extension blocks
- add P230, Y7, XYYY2101010, T430, XVUY210101010 formats
- don't call drop master on file close if not master
- use drm_printf_indent in atomic / bridge
- fix 32b format descriptions
- docs: fix toctree
- hdmi: add common TMDS character rates
- fix drm_syncobj_find_fence leak
rust:
- introduce Higher-Ranked lifetime types
- replace drvdata with scoped registration data
- add GPUVM immediate mode abstraction for rust GPU drivers
- introduce DeviceContext type state for drm::Device
bridge:
- clarify drm_bridge_get/put
- create drm_get_bridge_by_endpoint and use it
- analogix_dp: add panel probing
- ite-it6211 - use drm audio hdmi helpers
buddy:
- add lockdep annotations
dp:
- add PR and VRR updates
- mst: fix buffer overflows
- add Adaptive Sync SDP decoding support
- fix OOB reads in dp-mst
ttm:
- bump fpfn/lpfn to 64-bit
scheduler:
- change default to fair scheduler
- map runqueue 1:1 with scheduler
dma-buf:
- port selftests to kunit
- convert dma-buf system/heap allocators to module
- add separate DMABUF_HEAPS_SYSTEM_CC_SHARED Kconfig
udmabuf:
- revert hugetlb support
- fix error with CONFIG_DMA_API_DEBUG
dma-fence:
- fix tracepoints lifetime
- remove unused signal on any support
ras:
- add clear error counter netlink command to drm ras
gpusvm:
- reject VMAs with VM_IO or VM_PFNMAP when creating SVM ranges
- use IOVA allocations
pagemap:
- use IOVA allocations
panels:
- update to use ref counts
- add support for CSW PNB601LS1-2, LGD LP116WHA-SPB1
- add support for waveshare panels
- CMN N116BCN-EA1, CMN N140HCA-EEK, IVO M140NWFQ R5,
- IVO, R140NWFW R0, BOE NT140*, BOE NV133FHM-N4F,
- AUO B140*, AUO B133HAN06.6 and AUO B116XTN02.3 eDP panels
- Surface Pro 12 Panel
xe:
- add CRI PCI-IDs
- debugfs add multi-lrc info
- engine init cleanup
- PF fair scheduling auto provisioning
- system controller support for CRI/Xe3p
- PXP state machine fixes
- Reset/wedge/unload corner case fixes
- Wedge path memory allocation fixes
- PAT type cleanups
- Reject unsafe PAT for CPU cached memory
- OA improvements for CRI device memory
- kernel doc syntax in xe headers
- xe_drm.h documentation fixes
- include guard cleanups
- VF CCS memory pool
- i915/xe step unification
- Xe3p GT tuning fixes
- forcewake cleanup in GT and GuC
- admin-only PF mode
- enable hwmon energy attributes for CRI
- enable GT_MI_USER_INTERRUPT
- refactor emit functions
- oa workarounds
- multi_queue: allow QUEUE_TIMESTAMP register
- convert stolen memory to ttm range manager
- use xe2 style blitter as a feature flag
- make drm_driver const
- add/use IRQ page to HW engine definition
- fix oops when display disabled
i915:
- enable PIPEDMC_ERROR interrupt
- more common display code refactoring
- restructure DP/HDMI sink format handling
- eliminate FB usage from lowlevel pinning code
- panel replay bw optimization
- integrate sharpness filter into the scaler
- new fb_pin abstraction for xe/i915 fb transparent handling
- skip inactive MST connectors on HDCP
- start switching to display specific registers
- use polling when irq unavailable
- Adaptive-sync SDP prep
amdgpu:
- use drm_display_info for AMD VSDB data
- Initial HDMI 2.1 FRL support
- Initial DCN 4.2.1 support
- GART fixes for non-4k pages
- GC 11.5.6/SDMA 6.4.0/and other new IPs
- GFX9/DCE6/Hawaii/SDMA4/GART/Userq fixes
- Finish support for using multiple SDMA queues for TTM operations
- SWSMU updates
- GC 12.1 updates
- SMU 15.0.8 updates
- DCN 4.2 updates
- DC type conversion fixes
- Enable DC power module
- Replay/PSR updates
- SMU 13.x updates
- Compute queue quantum MQD updates
- ASPM fix
- Align VKMS with common implementation
- DC analog support fixes
- UVD 3 fixes
- TCC harvesting fixes for SI
- GC 11 APU module reload fix
- NBIO 6.3.2 support
- IH 7.1 updates
- DC cursor fixes
- VCN/JPEG user fence fixes
- DC support for connectors without DDC
- Prefer ROM BAR for default VGA device
- DC bandwidth fixes
- Add PTL support for profiler
- Introduce dc_plane_cm and migrate surface update color path
- Add FRL registers for HDMI 2.1
- Restructure VM state machine
- Auxless ALPM support
- GEM_OP locking/warning fixes
- switch to system_dfl_wq
amdkfd:
- GPUVM TLB flush fix
- Hotplug fix
- Boundary check fixes
- SVM fixes
- CRIU fixes
- add profiler API
- MES 12.1 updates
msm:
- core:
- fix shrinker documentation
- IFPC enabled for gen8
- PERFCNTR_CONFIG ioctl support
- GPU:
- reworked UBWC handling
- a810 support
- MDSS:
- add support for Milos platform
- reworked UBWC handling
- DisplayPort:
- reworked HPD handling as prep for MST
- DPU:
- Milos platform support
- reworked UBWC handling
- DSI:
- Milos platform support
nova:
- Hopper/Blackwell enablement (GH100/GB100/GB202)
- FSP support
- 32-bit firmware support
- HAL functions
- refactor GSP boot/unload
- GA100 support
- VBIOS hardening/refactoring
- Adopt higher order lifetime types
tyr:
- define register blocks
- add shmem backed GEM objects
- adopt higher order lifetime types
- move clock cleanup into Drop
radeon:
- Hawaii SMU fixes
- CS parser fix
- use struct drm_edid instead of edid
amdxdna:
- export per-client BO memory via fdinfo
- AIE4 device support
- support medium/lower power modes
- expandable device heap support
- revert read-only user-pointer BO mappings
ivpu:
- support frequency limiting
panthor:
- enable GEM shrinker support
- add eviction and reclaim info to fdinfo
v3d:
- enable runtime PM
mgag200:
- support XRGB1555 + C8
ast:
- support XRGB1555 + C8
- use constants for lots of registers
- fix register handling
imagination:
- fence handling refactoring
nouveau:
- fix sched double call
- expose VBIOS on GSP-RM systems
- add GA100 support
virtio:
- add VIRTIO_GPU_F_BLOB_ALIGNMENT flag
- add deferred mapping support
gud:
- add RCade Display Adapter
hibmc:
- fix no connectors usage
mediatek:
- hdmi: convert error handling
- simplify mtk_crtc allocation
exynos:
- move fbdev emulation to drm client buffers
- use drm format helpers for geometry/size
- adopt core DMA tracking
- fix framebuffer offset handling
renesas:
- add RZ/T2H SOC support
versilicon:
- add cursor plane support
tegra:
- use drm client for framebuffer"
* tag 'drm-next-2026-06-17' of https://gitlab.freedesktop.org/drm/kernel: (1731 commits)
dma-buf: move system_cc_shared heap under separate Kconfig
accel/amdxdna: Clear sva pointer after unbind
agp/amd64: Fix broken error propagation in agp_amd64_probe()
accel/amdxdna: Require carveout when PASID and force_iova are disabled
drm/amdkfd: always resume_all after suspend_all
drm/amdgpu/gfx: move fault and EOP IRQ get/put to hw_init/hw_fini
drm/amd/display: Consult MCCS FreeSync cap only if requested & supported
drm/amd/pm: Use strscpy in profile mode parsing
drm/amdkfd: Fix infinite loop parsing CRAT with zero subtype length
drm/amdkfd: fix sysfs topology prop length on buffer truncation
drm/amdgpu: drop retry loop in amdgpu_hmm_range_get_pages
drm/amd/pm: bound OD parameter parsing to stack array size
drm/amd/pm: Stop pp_od_clk_voltage emit at PAGE_SIZE
drm/amdkfd: Unwind debug trap enable on copy_to_user failure
drm/amdgpu: validate the mes firmware version for gfx12.1
drm/amdgpu: validate the mes firmware version for gfx12
drm/amdgpu: compare MES firmware version ucode for gfx11
drm/amdkfd: Add bounds check for AMDKFD_IOC_WAIT_EVENTS
drm/amdgpu: restart the CS if some parts of the VM are still invalidated
drm/amd/display: use unsigned types for local pipe and REG_GET counters
...
Diffstat (limited to 'rust/kernel')
| -rw-r--r-- | rust/kernel/drm/device.rs | 252 | ||||
| -rw-r--r-- | rust/kernel/drm/driver.rs | 49 | ||||
| -rw-r--r-- | rust/kernel/drm/gem/mod.rs | 72 | ||||
| -rw-r--r-- | rust/kernel/drm/gem/shmem.rs | 61 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/mod.rs | 328 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/sm_ops.rs | 429 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/va.rs | 168 | ||||
| -rw-r--r-- | rust/kernel/drm/gpuvm/vm_bo.rs | 249 | ||||
| -rw-r--r-- | rust/kernel/drm/mod.rs | 5 |
9 files changed, 1501 insertions, 112 deletions
diff --git a/rust/kernel/drm/device.rs b/rust/kernel/drm/device.rs index 403fc35353c7..477cf771fb10 100644 --- a/rust/kernel/drm/device.rs +++ b/rust/kernel/drm/device.rs @@ -6,10 +6,12 @@ use crate::{ alloc::allocator::Kmalloc, - bindings, device, + bindings, + device, drm::{ self, - driver::AllocImpl, // + driver::AllocImpl, + private::Sealed, // }, error::from_err_ptr, prelude::*, @@ -17,16 +19,20 @@ use crate::{ ARef, AlwaysRefCounted, // }, - types::Opaque, + types::{ + NotThreadSafe, + Opaque, // + }, workqueue::{ HasDelayedWork, HasWork, Work, WorkItem, // - }, + }, // }; use core::{ alloc::Layout, + marker::PhantomData, mem, ops::Deref, ptr::{ @@ -66,36 +72,122 @@ macro_rules! drm_legacy_fields { } } -/// A typed DRM device with a specific `drm::Driver` implementation. +/// 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`]. +/// +/// Each stage of this process is described below: +/// +/// ```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. +/// +/// 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. /// -/// The device is always reference-counted. +/// 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. +/// +/// 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. /// /// # Invariants /// -/// `self.dev` is a valid instance of a `struct device`. -#[repr(C)] -pub struct Device<T: drm::Driver> { - dev: Opaque<bindings::drm_device>, - data: T::Data, +/// A [`Device`] in this [`DeviceContext`] is guaranteed to have been registered with userspace +/// at some point in time. +pub struct Registered; + +impl Sealed for Registered {} +impl DeviceContext for Registered {} + +/// The [`DeviceContext`] of a [`Device`] that may be unregistered and partly uninitialized. +/// +/// 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 can be used to create a +/// [`Registration`](drm::driver::Registration). +pub struct Uninit; + +impl Sealed for Uninit {} +impl DeviceContext for Uninit {} + +/// A [`Device`] which is known at compile-time to be unregistered with userspace. +/// +/// This type allows performing operations which are only safe to do before userspace registration, +/// and can be used to create a [`Registration`](drm::driver::Registration) once the driver is ready +/// to register the device with userspace. +/// +/// Since DRM device initialization must be single-threaded, this object is not thread-safe. +/// +/// # Invariants +/// +/// 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); + +impl<T: drm::Driver> Deref for UnregisteredDevice<T> { + type Target = Device<T, Uninit>; + + fn deref(&self) -> &Self::Target { + &self.0 + } } -impl<T: drm::Driver> Device<T> { +impl<T: drm::Driver> UnregisteredDevice<T> { + const fn compute_features() -> u32 { + let mut features = drm::driver::FEAT_GEM; + + if T::FEAT_RENDER { + features |= drm::driver::FEAT_RENDER; + } + + features + } + const VTABLE: bindings::drm_driver = drm_legacy_fields! { load: None, open: Some(drm::File::<T::File>::open_callback), postclose: Some(drm::File::<T::File>::postclose_callback), unload: None, - release: Some(Self::release), + release: Some(Device::<T>::release), master_set: None, master_drop: None, debugfs_init: None, - 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, + + // 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, + show_fdinfo: None, fbdev_probe: None, @@ -105,7 +197,7 @@ impl<T: drm::Driver> Device<T> { name: crate::str::as_char_ptr_in_const_context(T::INFO.name).cast_mut(), desc: crate::str::as_char_ptr_in_const_context(T::INFO.desc).cast_mut(), - driver_features: drm::driver::FEAT_GEM, + driver_features: Self::compute_features(), ioctls: T::IOCTLS.as_ptr(), num_ioctls: T::IOCTLS.len() as i32, fops: &Self::GEM_FOPS, @@ -113,11 +205,13 @@ impl<T: drm::Driver> Device<T> { const GEM_FOPS: bindings::file_operations = drm::gem::create_fops(); - /// Create a new `drm::Device` for a `drm::Driver`. - pub fn new(dev: &device::Device, data: impl PinInit<T::Data, Error>) -> Result<ARef<Self>> { + /// 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> { // `__drm_dev_alloc` uses `kmalloc()` to allocate memory, hence ensure a `kmalloc()` // compatible `Layout`. - let layout = Kmalloc::aligned_layout(Layout::new::<Self>()); + let layout = Kmalloc::aligned_layout(Layout::new::<Device<T, Uninit>>()); // Use a temporary vtable without a `release` callback until `data` is initialized, so // init failure can release the DRM device without dropping uninitialized fields. @@ -129,12 +223,12 @@ impl<T: drm::Driver> Device<T> { // SAFETY: // - `alloc_vtable` reference remains valid until no longer used, // - `dev` is valid by its type invarants, - let raw_drm: *mut Self = unsafe { + let raw_drm: *mut Device<T, Uninit> = unsafe { bindings::__drm_dev_alloc( dev.as_raw(), &alloc_vtable, layout.size(), - mem::offset_of!(Self, dev), + mem::offset_of!(Device<T, Uninit>, dev), ) } .cast(); @@ -142,7 +236,7 @@ impl<T: drm::Driver> Device<T> { // SAFETY: `raw_drm` is a valid pointer to `Self`, given that `__drm_dev_alloc` was // successful. - let drm_dev = unsafe { Self::into_drm_device(raw_drm) }; + let drm_dev = unsafe { Device::into_drm_device(raw_drm) }; // SAFETY: `raw_drm` is a valid pointer to `Self`. let raw_data = unsafe { ptr::addr_of_mut!((*raw_drm.as_ptr()).data) }; @@ -161,9 +255,39 @@ impl<T: drm::Driver> Device<T> { // SAFETY: The reference count is one, and now we take ownership of that reference as a // `drm::Device`. - Ok(unsafe { ARef::from_raw(raw_drm) }) + // INVARIANT: We just created the device above, but have yet to call `drm_dev_register`. + // `Self` cannot be copied or sent to another thread - ensuring that `drm_dev_register` + // won't be called during its lifetime and that the device is unregistered. + Ok(Self(unsafe { ARef::from_raw(raw_drm) }, NotThreadSafe)) } +} +/// 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. +/// +/// # Invariants +/// +/// * `self.dev` is a valid instance of a `struct device`. +/// * 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> { + dev: Opaque<bindings::drm_device>, + data: T::Data, + _ctx: PhantomData<C>, +} + +impl<T: drm::Driver, C: DeviceContext> Device<T, C> { pub(crate) fn as_raw(&self) -> *mut bindings::drm_device { self.dev.get() } @@ -189,13 +313,13 @@ impl<T: drm::Driver> Device<T> { /// /// # Safety /// - /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count, - /// i.e. it must be ensured that the reference count of the C `struct drm_device` `ptr` points - /// to can't drop to zero, for the duration of this function call and the entire duration when - /// the returned reference exists. - /// - /// Additionally, callers must ensure that the `struct device`, `ptr` is pointing to, is - /// embedded in `Self`. + /// * Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count, + /// i.e. it must be ensured that the reference count of the C `struct drm_device` `ptr` points + /// to can't drop to zero, for the duration of this function call and the entire duration when + /// the returned reference exists. + /// * Additionally, callers must ensure that the `struct device`, `ptr` is pointing to, is + /// embedded in `Self`. + /// * Callers promise that any type invariants of `C` will be upheld. #[doc(hidden)] pub unsafe fn from_raw<'a>(ptr: *const bindings::drm_device) -> &'a Self { // SAFETY: By the safety requirements of this function `ptr` is a valid pointer to a @@ -215,9 +339,20 @@ impl<T: drm::Driver> Device<T> { // - `this` is valid for dropping. unsafe { core::ptr::drop_in_place(this) }; } + + /// Change the [`DeviceContext`] for a [`Device`]. + /// + /// # Safety + /// + /// The caller promises that `self` fulfills all of the guarantees provided by the given + /// [`DeviceContext`]. + pub(crate) unsafe fn assume_ctx<NewCtx: DeviceContext>(&self) -> &Device<T, NewCtx> { + // SAFETY: The data layout is identical via our type invariants. + unsafe { mem::transmute(self) } + } } -impl<T: drm::Driver> Deref for Device<T> { +impl<T: drm::Driver, C: DeviceContext> Deref for Device<T, C> { type Target = T::Data; fn deref(&self) -> &Self::Target { @@ -227,7 +362,7 @@ impl<T: drm::Driver> Deref for Device<T> { // SAFETY: DRM device objects are always reference counted and the get/put functions // satisfy the requirements. -unsafe impl<T: drm::Driver> AlwaysRefCounted for Device<T> { +unsafe impl<T: drm::Driver, C: DeviceContext> AlwaysRefCounted for Device<T, C> { 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()) }; @@ -242,7 +377,7 @@ unsafe impl<T: drm::Driver> AlwaysRefCounted for Device<T> { } } -impl<T: drm::Driver> AsRef<device::Device> for Device<T> { +impl<T: drm::Driver, C: DeviceContext> AsRef<device::Device> for Device<T, C> { fn as_ref(&self) -> &device::Device { // SAFETY: `bindings::drm_device::dev` is valid as long as the DRM device itself is valid, // which is guaranteed by the type invariant. @@ -251,21 +386,22 @@ impl<T: drm::Driver> AsRef<device::Device> for Device<T> { } // SAFETY: A `drm::Device` can be released from any thread. -unsafe impl<T: drm::Driver> Send for Device<T> {} +unsafe impl<T: drm::Driver, C: DeviceContext> Send for Device<T, C> {} // SAFETY: A `drm::Device` can be shared among threads because all immutable methods are protected // by the synchronization in `struct drm_device`. -unsafe impl<T: drm::Driver> Sync for Device<T> {} +unsafe impl<T: drm::Driver, C: DeviceContext> Sync for Device<T, C> {} -impl<T, const ID: u64> WorkItem<ID> for Device<T> +impl<T, C, const ID: u64> WorkItem<ID> for Device<T, C> where T: drm::Driver, - T::Data: WorkItem<ID, Pointer = ARef<Device<T>>>, - T::Data: HasWork<Device<T>, ID>, + T::Data: WorkItem<ID, Pointer = ARef<Self>>, + T::Data: HasWork<Self, ID>, + C: DeviceContext, { - type Pointer = ARef<Device<T>>; + type Pointer = ARef<Self>; - fn run(ptr: ARef<Device<T>>) { + fn run(ptr: ARef<Self>) { T::Data::run(ptr); } } @@ -277,40 +413,42 @@ where // stored inline in `drm::Device`, so the `container_of` call is valid. // // - The two methods are true inverses of each other: given `ptr: *mut -// Device<T>`, `raw_get_work` will return a `*mut Work<Device<T>, ID>` through -// `T::Data::raw_get_work` and given a `ptr: *mut Work<Device<T>, ID>`, -// `work_container_of` will return a `*mut Device<T>` through `container_of`. -unsafe impl<T, const ID: u64> HasWork<Device<T>, ID> for Device<T> +// Device<T, C>`, `raw_get_work` will return a `*mut Work<Device<T, C>, ID>` through +// `T::Data::raw_get_work` and given a `ptr: *mut Work<Device<T, C>, ID>`, +// `work_container_of` will return a `*mut Device<T, C>` through `container_of`. +unsafe impl<T, C, const ID: u64> HasWork<Self, ID> for Device<T, C> where T: drm::Driver, - T::Data: HasWork<Device<T>, ID>, + T::Data: HasWork<Self, ID>, + C: DeviceContext, { - unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<Device<T>, ID> { - // SAFETY: The caller promises that `ptr` points to a valid `Device<T>`. + unsafe fn raw_get_work(ptr: *mut Self) -> *mut Work<Self, ID> { + // SAFETY: The caller promises that `ptr` points to a valid `Device<T, C>`. let data_ptr = unsafe { &raw mut (*ptr).data }; // SAFETY: `data_ptr` is a valid pointer to `T::Data`. unsafe { T::Data::raw_get_work(data_ptr) } } - unsafe fn work_container_of(ptr: *mut Work<Device<T>, ID>) -> *mut Self { + unsafe fn work_container_of(ptr: *mut Work<Self, ID>) -> *mut Self { // SAFETY: The caller promises that `ptr` points at a `Work` field in // `T::Data`. let data_ptr = unsafe { T::Data::work_container_of(ptr) }; - // SAFETY: `T::Data` is stored as the `data` field in `Device<T>`. + // SAFETY: `T::Data` is stored as the `data` field in `Device<T, C>`. unsafe { crate::container_of!(data_ptr, Self, data) } } } // SAFETY: Our `HasWork<T, ID>` implementation returns a `work_struct` that is // stored in the `work` field of a `delayed_work` with the same access rules as -// the `work_struct` owing to the bound on `T::Data: HasDelayedWork<Device<T>, +// the `work_struct` owing to the bound on `T::Data: HasDelayedWork<Device<T, C>, // ID>`, which requires that `T::Data::raw_get_work` return a `work_struct` that // is inside a `delayed_work`. -unsafe impl<T, const ID: u64> HasDelayedWork<Device<T>, ID> for Device<T> +unsafe impl<T, C, const ID: u64> HasDelayedWork<Self, ID> for Device<T, C> where T: drm::Driver, - T::Data: HasDelayedWork<Device<T>, ID>, + T::Data: HasDelayedWork<Self, ID>, + C: DeviceContext, { } diff --git a/rust/kernel/drm/driver.rs b/rust/kernel/drm/driver.rs index 5233bdebc9fc..25f7e233884d 100644 --- a/rust/kernel/drm/driver.rs +++ b/rust/kernel/drm/driver.rs @@ -13,9 +13,15 @@ use crate::{ prelude::*, sync::aref::ARef, // }; +use core::{ + mem, + 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; +/// Driver supports render nodes, i.e.: /dev/dri/renderDXX devices. +pub(crate) const FEAT_RENDER: u32 = bindings::drm_driver_feature_DRIVER_RENDER; /// Information data for a DRM Driver. pub struct DriverInfo { @@ -105,7 +111,7 @@ pub trait Driver { type Data: Sync + Send; /// The type used to manage memory for this driver. - type Object: AllocImpl; + type Object<Ctx: drm::DeviceContext>: AllocImpl; /// The type used to represent a DRM File (client) type File: drm::file::DriverFile; @@ -115,6 +121,16 @@ pub trait Driver { /// IOCTL list. See `kernel::drm::ioctl::declare_drm_ioctls!{}`. const IOCTLS: &'static [drm::ioctl::DrmIoctlDescriptor]; + + /// 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 + /// 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 + /// userspace processes using the master node can invoke any ioctl. + const FEAT_RENDER: bool = false; } /// The registration type of a `drm::Device`. @@ -123,21 +139,31 @@ pub trait Driver { pub struct Registration<T: Driver>(ARef<drm::Device<T>>); impl<T: Driver> Registration<T> { - fn new(drm: &drm::Device<T>, flags: usize) -> Result<Self> { + 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) })?; - Ok(Self(drm.into())) + // 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)) } - /// Registers a new [`Device`](drm::Device) with userspace. + /// Registers a new [`UnregisteredDevice`](drm::UnregisteredDevice) with userspace. /// /// Ownership of the [`Registration`] object is passed to [`devres::register`]. - pub fn new_foreign_owned( - drm: &drm::Device<T>, - dev: &device::Device<device::Bound>, + pub fn new_foreign_owned<'a>( + drm: drm::UnregisteredDevice<T>, + dev: &'a device::Device<device::Bound>, flags: usize, - ) -> Result + ) -> Result<&'a drm::Device<T>> where T: 'static, { @@ -146,8 +172,13 @@ impl<T: Driver> Registration<T> { } let reg = Registration::<T>::new(drm, flags)?; + let drm = NonNull::from(reg.device()); + + devres::register(dev, reg, GFP_KERNEL)?; - devres::register(dev, reg, GFP_KERNEL) + // 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() }) } /// Returns a reference to the `Device` instance for this registration. diff --git a/rust/kernel/drm/gem/mod.rs b/rust/kernel/drm/gem/mod.rs index 01b5bd47a333..c8b66d816871 100644 --- a/rust/kernel/drm/gem/mod.rs +++ b/rust/kernel/drm/gem/mod.rs @@ -8,6 +8,10 @@ use crate::{ bindings, drm::{ self, + device::{ + DeviceContext, + Registered, // + }, driver::{ AllocImpl, AllocOps, // @@ -22,6 +26,7 @@ use crate::{ types::Opaque, }; use core::{ + marker::PhantomData, ops::Deref, ptr::NonNull, // }; @@ -73,6 +78,12 @@ pub(crate) use impl_aref_for_gem_obj; /// [`DriverFile`]: drm::file::DriverFile pub type DriverFile<T> = drm::File<<<T as DriverObject>::Driver as drm::Driver>::File>; +/// A type alias for retrieving the current [`AllocImpl`] for a given [`DriverObject`]. +/// +/// [`Driver`]: drm::Driver +pub type DriverAllocImpl<T, Ctx = Registered> = + <<T as DriverObject>::Driver as drm::Driver>::Object<Ctx>; + /// GEM object functions, which must be implemented by drivers. pub trait DriverObject: Sync + Send + Sized { /// Parent `Driver` for this object. @@ -82,19 +93,19 @@ pub trait DriverObject: Sync + Send + Sized { type Args; /// Create a new driver data object for a GEM object of a given size. - fn new( - dev: &drm::Device<Self::Driver>, + fn new<Ctx: DeviceContext>( + dev: &drm::Device<Self::Driver, Ctx>, size: usize, args: Self::Args, ) -> impl PinInit<Self, Error>; /// Open a new handle to an existing object, associated with a File. - fn open(_obj: &<Self::Driver as drm::Driver>::Object, _file: &DriverFile<Self>) -> Result { + fn open(_obj: &DriverAllocImpl<Self>, _file: &DriverFile<Self>) -> Result { Ok(()) } /// Close a handle to an existing object, associated with a File. - fn close(_obj: &<Self::Driver as drm::Driver>::Object, _file: &DriverFile<Self>) {} + fn close(_obj: &DriverAllocImpl<Self>, _file: &DriverFile<Self>) {} } /// Trait that represents a GEM object subtype @@ -120,9 +131,12 @@ extern "C" fn open_callback<T: DriverObject>( // SAFETY: `open_callback` is only ever called with a valid pointer to a `struct drm_file`. let file = unsafe { DriverFile::<T>::from_raw(raw_file) }; - // SAFETY: `open_callback` is specified in the AllocOps structure for `DriverObject<T>`, - // ensuring that `raw_obj` is contained within a `DriverObject<T>` - let obj = unsafe { <<T::Driver as drm::Driver>::Object as IntoGEMObject>::from_raw(raw_obj) }; + // SAFETY: + // * `open_callback` is specified in the AllocOps structure for `DriverObject`, ensuring that + // `raw_obj` is contained within a `DriverAllocImpl<T>` + // * It is only possible for `open_callback` to be called after device registration, ensuring + // that the object's device is in the `Registered` state. + let obj: &DriverAllocImpl<T> = unsafe { IntoGEMObject::from_raw(raw_obj) }; match T::open(obj, file) { Err(e) => e.to_errno(), @@ -139,12 +153,12 @@ extern "C" fn close_callback<T: DriverObject>( // SAFETY: `close_callback` is specified in the AllocOps structure for `Object<T>`, ensuring // that `raw_obj` is indeed contained within a `Object<T>`. - let obj = unsafe { <<T::Driver as drm::Driver>::Object as IntoGEMObject>::from_raw(raw_obj) }; + let obj: &DriverAllocImpl<T> = unsafe { IntoGEMObject::from_raw(raw_obj) }; T::close(obj, file); } -impl<T: DriverObject> IntoGEMObject for Object<T> { +impl<T: DriverObject, Ctx: DeviceContext> IntoGEMObject for Object<T, Ctx> { fn as_raw(&self) -> *mut bindings::drm_gem_object { self.obj.get() } @@ -152,7 +166,7 @@ impl<T: DriverObject> IntoGEMObject for Object<T> { unsafe fn from_raw<'a>(self_ptr: *mut bindings::drm_gem_object) -> &'a Self { // SAFETY: `obj` is guaranteed to be in an `Object<T>` via the safety contract of this // function - unsafe { &*crate::container_of!(Opaque::cast_from(self_ptr), Object<T>, obj) } + unsafe { &*crate::container_of!(Opaque::cast_from(self_ptr), Object<T, Ctx>, obj) } } } @@ -169,7 +183,7 @@ pub trait BaseObject: IntoGEMObject { fn create_handle<D, F>(&self, file: &drm::File<F>) -> Result<u32> where Self: AllocImpl<Driver = D>, - D: drm::Driver<Object = Self, File = F>, + D: drm::Driver<Object<Registered> = Self, File = F>, F: drm::file::DriverFile<Driver = D>, { let mut handle: u32 = 0; @@ -184,7 +198,7 @@ pub trait BaseObject: IntoGEMObject { fn lookup_handle<D, F>(file: &drm::File<F>, handle: u32) -> Result<ARef<Self>> where Self: AllocImpl<Driver = D>, - D: drm::Driver<Object = Self, File = F>, + D: drm::Driver<Object<Registered> = Self, File = F>, F: drm::file::DriverFile<Driver = D>, { // SAFETY: The arguments are all valid per the type invariants. @@ -236,16 +250,18 @@ impl<T: IntoGEMObject> BaseObjectPrivate for T {} /// /// # Invariants /// -/// - `self.obj` is a valid instance of a `struct drm_gem_object`. +/// * `self.obj` is a valid instance of a `struct drm_gem_object`. +/// * Any type invariants of `Ctx` apply to the parent DRM device for this GEM object. #[repr(C)] #[pin_data] -pub struct Object<T: DriverObject + Send + Sync> { +pub struct Object<T: DriverObject + Send + Sync, Ctx: DeviceContext = Registered> { obj: Opaque<bindings::drm_gem_object>, #[pin] data: T, + _ctx: PhantomData<Ctx>, } -impl<T: DriverObject> Object<T> { +impl<T: DriverObject, Ctx: DeviceContext> Object<T, Ctx> { const OBJECT_FUNCS: bindings::drm_gem_object_funcs = bindings::drm_gem_object_funcs { free: Some(Self::free_callback), open: Some(open_callback::<T>), @@ -265,11 +281,16 @@ impl<T: DriverObject> Object<T> { }; /// Create a new GEM object. - pub fn new(dev: &drm::Device<T::Driver>, size: usize, args: T::Args) -> Result<ARef<Self>> { |
