Files
Linus Torvalds 4e69c1856b Merge tag 'drm-next-2026-08-20' of https://gitlab.freedesktop.org/drm/kernel
Pull drm updates from Dave Airlie:
 "Highlights:

   - dmemcg eviction support is good for low VRAM things like Steam
     Machine

   - AMD adds gfx6-8 modifier support for older GPUs that enables a
     bunch of wayland stuff

   - i915/xe has some new hw support but also a lot of display
     refactoring

  Everything:

  perf:
   - export perf_allow_ APIs for xe

  udmabuf:
   - remove default size limit of 64MB

  rust:
   - i/o rework (signed tag from driver-core tree)
   - add registration guard and registration data
   - fix unbounded lifetimes in ioctl handler args
   - fix a drm_dev_register race
   - gem_shmem: add DmaResvGuard helper
   - gpuvm: require send/sync for driver data
       - implement send/sync for GpuVaAlloc and GpuVmBo
       - add SmContext lifetime
   - rename dma_handle to dma_address
   - change pci_sriov_get_totalvfs return to unsigned int

  core:
   - create drm_of_get_panel_orientation
   - send per-connector hotplug events
   - add thunderbolt UBHR tunneling support

  connector:
   - add color format property

  dmem:
   - introduce a peak file
   - accept one region per limit
   - add dmemcg support for eviction

  gpusvm:
   - reorg code to give drivers more flexibility

  atomic:
   - add create_state callback and helper
   - add documentation on atomic commit lifetime

  buddy:
   - add per-order free
   - add used block scoreboard
   - fix UAF
   - test buffer clearance on resume
   - add phys_addr->block helper

  gem:
   - drop DRIVER_GEM_GPUVA flag

  ttm:
   - be more aggressive allocating below protection limit

  sched:
   - add test suite for concurrent job submissions

  hdmi:
   - hook the color format property in helpers

  mipi-dsi:
   - add MIPI_DSI_MODE_DSC_ALL_SLICES_IN_PKT

  bridge:
   - add atomic create callbacks
   - drop atomic reset
   - display-connector: don't autoenable HPD IRQ
   - trigger initial HPD for DP
   - ti-sn65dsi83: remove NO_HFP and NO_HBP mode flags
   - analogix_dp: switch to DP link training helpers

  dp:
   - add support for DSC max delta BPP

  edid:
   - parse panel type from DisplayID 2.x Display Parameters

  sysfb:
   - improve panel, stride, framebuffer size validation

  panel:
   - implement ref counting for struct drm_panel
   - himax-hx83121a: add backlight regulator support
   - novatek-nt36672a: Inline panel init sequences
   - visionox-vtdr6130: enable DSC
   - novatek-nt37801: Use mipi_dsi_*_multi() functions
   - samsung-s6d16d0: Fix prepare error handling
   - support Novatek NT36536 plus DT bindings
   - sofef00: fix backlight updates
   - osd101t2587: use mipi_dsi_*_multi interface
   - panel-edp: adjust timing for AUO displays
   - panel-lvds: support Opto Logic SCX1001511GGC49
   - panel-simple: support Kyocera tcg070wvlq
   - panel-edp: quirks
       - AUO B116XAT04.3, CMN N116BCP-EA2, CSW MNB601LS1-8
       - BOE NV116WH2-M30, BOE NT116WHM-N21, BOE NV116FH1-M31
       - BOE NV116FH1-M30, NV140FHM-N5B, TM156VDXP25
       - BOE NE160QDM-NY1, MB116AS01
   - new:
       - Samsung ATNA40HQ08-0, Anbernic TD4310
       - Chipone ICNA35XX, Ilitek ILI9488
       - Ilitek ILI7807S, Renesas R63419
       - MNE001BS6-2, MNF601BS4-1, Sharp LQ120P1JX51

  virtio:
   - add support for save/restore virtio_gpu_objects
   - abort vq wait on device removal

  amdgpu:
   - add color format DRM property
   - initial compute pipe reset support
   - add GFX 6-8 modifier support
   - initial DCN 6.0.0 support
   - dmemcg eviction support
   - improved boundary checking for bios parsing
   - RAS updates and rework
   - VCN secure submission fixes
   - 8K panel fix
   - Display KUNIT tests
   - parse panel type from DisplayID
   - Align IP discovery to pci device lifetime
   - SOC15 register macro cleanups
   - UVD memory placement fixes
   - GFX9 mode2 reset fixes
   - drop unnecessary BUG/BUG_ON
   - GFX8 soft reset rework
   - enable soft reset on GFX8
   - PSP/SMU 15.0.9 update
   - VI ASPM fix
   - userq fixes
   - amdgpu_vm_get_task_info_pasid lifetime fix
   - DC CACP support
   - change system_unbound_wq with system_dfl_wq
   - Loosen VFCT bios parsing to deal with pci=realloc
   - SI/SMU7 AC/DC switch fix
   - VM fence handling fix
   - GEM close optimisation
   - Apple Studio Display fixes
   - DC FRL fixes

  amdkfd:
   - initial compute pipe reset support
   - allow applications to opt out of sigbus on fatal errors
   - improve CRIU boundary checks
   - MQD handling rework
   - move TBA/TMA from system to device memory
   - avoid topology-lock in kfd_mmap
   - SVM eviction fixes

  radeon:
   - fix unset CONFIG_ACPI build

  i915:
   - Novalake (NVL display version 35) timing generator enabling
   - NVL DC3CO enabling
   - enable UBHR link rates on thunderbolt tunnels
   - Reduce Xe3+ PM demand peak bandwidth
   - enable pipe DMC error interrupts for display 30+
   - add kunit tests for DP link config selection
   - refactor and document DP link recovery
   - i915/xe driver display probe/remove/suspend/resume/shutdown cleanup
     and unification
   - i915/xe display runtime PM unified
   - Break i915 and xe panic dependency on struct intel_framebuffer
   - Streamline Pre/Post-CSC LUT loops
   - drop TGL DC3DO support
   - CDCLK santization
   - fix HDMI scrambling enable
   - fix phys bo pread/pwrite with offset
   - add missing nospec on parallel submit slot
   - fix some NULL derefs

  xe:
   - drop force_execlist module param
   - gate observation streams with perf_allow_cpu
   - skip FORCE_WC and vm_bound check for external dma-bufs
   - dmemcg eviction support
   - remove unused NVL-S GuC
   - TLB invalidation improvements
   - NVL-S updated PCI-IDs and w/a
   - madvise: optimise invalidation path
   - fix infinite gt-reset loop in timeout recovery
   - update TTM device benefical_order
   - wait on external BO kernel fences in exec ioctl
   - add/use more KLV helpers
   - sriov: disable display in admin only PF mode
   - add RAS GPU health indicator
   - optimise TTM populate for DONTNEED BO
   - drop force_probe for NVL-s
   - add debugfs for pcode info

  amdxdna:
   - disable device buffer export

  nova:
   - build nova-core/nova-drm from drivers/gpu
   - export nova-core rust symbols (workaround)
   - GSP boot process consolidation
   - Boot GSP with vGPU enabled
   - TLV firmware image format support
   - Hopper/Blackwell fixes and cleanups
   - I/O projection adoption

  tyr:
   - firmware loading and MCU boot
   - add generic slot manager + MMU
   - GPU VM support ARM64 LPAE page tables
   - add kernel buffer object for internal allocations
   - add parser for Mali CSF
   - add MCU booting

  nouveau:
   - race fixes
   - check instmem iomapping at first use
   - add dmemcg support
   - expose NVDEC channels
   - add scanline position/head state support for GSP

  qxl:
   - convert simple encoder to regular

  ethosu:
   - add perf counter support

  etnaviv:
   - force flush on power register ops

  msm:
   - support DSC configuration with slice_per_pkt > 1

  mxsfb:
   - fix disable sequence

  panthor:
   - support sparse mappings

  rockchip:
   - switch away from simple helpers
   - support YUV background color
   - fix layer config timeout
   - add edp support for rk3576
   - add batch command submission function

  rocket:
   - error handling and NULL ptr deref fixes

  sun4i:
   - switch away from simple helpers

  imagination:
   - mark BXM-4-64 MC1 as support

  host1x:
   - support tegra264

  tegra:
   - add DSI for tegra 20/30

  v3d:
   - reduce PM runtime autosuspend delay
   - scheduler fixes and refactoring
   - deprecate v3d 3.3 and 4.1
   - validate CPU job query boundaries

  hibmc:
   - improve plane format handling
   - switch to gem shmem

  mediatek:
   - cec: correct compat for mt7623-8167?

  exynos:
   - remove simple dependency
   - add error handling to encoder paths
   - take i2c adapter module reference"

* tag 'drm-next-2026-08-20' of https://gitlab.freedesktop.org/drm/kernel: (2074 commits)
  drm/xe/mcr: Take vcs1/vecs1 into account for first media slice
  drm/xe: Fix a bug in pc_adjust_freq_bounds()
  drm/xe: Fix xe_device_probe() failure
  drm/xe/drm_ras: Move has_drm_ras check to drm_ras layer
  drm/xe/ras: Fix boot-time ras error processing
  drm/amd/display: make DC_RUN_WITH_PREEMPTION_ENABLED misuse a build error
  drm/amd/pm: silence uninitialized variable warnings
  drm/amdgpu: skip BOs being torn down during GTT recovery
  drm/amdgpu: Reject UVD message with invalid number of h265 refs
  drm/amdgpu: keep PRT mappings off the vm_bo state lists
  drm/amdgpu: fix nbif 6.3.1 l1 low power not functional
  drm/amd/display: fix BT.2020 YCbCr output CSC matrices for DCE
  drm/amd/display: fix BT.2020 YCbCr limited output CSC matrix
  drm/amdgpu: Implement insert_end for VCE 3
  drm/amdgpu: Fix UVD min buffer sizes
  drm/amdgpu: Fix UVD decode image min size calculation
  drm/amdgpu: Fix UVD dpb min size calculation for H264
  drm/amdgpu: Reject UVD message with dimensions above 4096
  drm/amdgpu: check ASPM on the dGPU host link
  drm/radeon: fix autosuspend cleanup during teardown
  ...
2026-08-21 08:41:00 -07:00

259 lines
10 KiB
Rust

// SPDX-License-Identifier: GPL-2.0 OR MIT
use super::*;
/// Represents that a given GEM object has at least one mapping on this [`GpuVm`] instance.
///
/// Does not assume that GEM lock is held.
///
/// # Invariants
///
/// * Allocated with `kmalloc` and refcounted via `inner`.
/// * Is present in the gem list.
#[repr(C)]
#[pin_data]
pub struct GpuVmBo<T: DriverGpuVm> {
#[pin]
inner: Opaque<bindings::drm_gpuvm_bo>,
#[pin]
data: T::VmBoData,
}
// SAFETY: It is safe to send a `GpuVmBo<T>` to another thread: dropping it there drops
// `T::VmBoData` and the GEM `T::Object`, both `Send` by the `DriverGpuVm` bounds.
unsafe impl<T: DriverGpuVm> Send for GpuVmBo<T> {}
// SAFETY: It is safe to share a `&GpuVmBo<T>` between threads: it effectively shares
// `&T::VmBoData` and the GEM `&T::Object` (both `Sync`), and any thread may upgrade to an
// `ARef` and ultimately drop them (both `Send`), per the `DriverGpuVm` bounds.
unsafe impl<T: DriverGpuVm> Sync for GpuVmBo<T> {}
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.inner`.
unsafe impl<T: DriverGpuVm> AlwaysRefCounted for GpuVmBo<T> {
fn inc_ref(&self) {
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.inner`.
unsafe { bindings::drm_gpuvm_bo_get(self.inner.get()) };
}
unsafe fn dec_ref(obj: NonNull<Self>) {
// CAST: `drm_gpuvm_bo` is first field of repr(C) struct.
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.inner`.
// This GPUVM instance uses immediate mode, so we may put the refcount using the deferred
// mechanism.
unsafe { bindings::drm_gpuvm_bo_put_deferred(obj.as_ptr().cast()) };
}
}
impl<T: DriverGpuVm> PartialEq for GpuVmBo<T> {
#[inline]
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.as_raw(), other.as_raw())
}
}
impl<T: DriverGpuVm> Eq for GpuVmBo<T> {}
impl<T: DriverGpuVm> GpuVmBo<T> {
/// The function pointer for allocating a GpuVmBo stored in the gpuvm vtable.
///
/// Allocation is always implemented according to [`Self::vm_bo_alloc`], but it is set to
/// `None` if the default gpuvm behavior is the same as `vm_bo_alloc`.
///
/// This may be `Some` even if `FREE_FN` is `None`, or vice-versa.
pub(super) const ALLOC_FN: Option<unsafe extern "C" fn() -> *mut bindings::drm_gpuvm_bo> = {
use core::alloc::Layout;
let base = Layout::new::<bindings::drm_gpuvm_bo>();
let rust = Layout::new::<Self>();
assert!(base.size() <= rust.size());
if base.size() != rust.size() || base.align() != rust.align() {
Some(Self::vm_bo_alloc)
} else {
// This causes GPUVM to allocate a `GpuVmBo<T>` with `kzalloc(sizeof(drm_gpuvm_bo))`.
None
}
};
/// The function pointer for freeing a GpuVmBo stored in the gpuvm vtable.
///
/// Freeing is always implemented according to [`Self::vm_bo_free`], but it is set to `None` if
/// the default gpuvm behavior is the same as `vm_bo_free`.
///
/// This may be `Some` even if `ALLOC_FN` is `None`, or vice-versa.
pub(super) const FREE_FN: Option<unsafe extern "C" fn(*mut bindings::drm_gpuvm_bo)> = {
if core::mem::needs_drop::<Self>() {
Some(Self::vm_bo_free)
} else {
// This causes GPUVM to free a `GpuVmBo<T>` with `kfree`.
None
}
};
/// Custom function for allocating a `drm_gpuvm_bo`.
///
/// # Safety
///
/// Always safe to call.
unsafe extern "C" fn vm_bo_alloc() -> *mut bindings::drm_gpuvm_bo {
let raw_ptr = KBox::<Self>::new_uninit(GFP_KERNEL | __GFP_ZERO)
.map(KBox::into_raw)
.unwrap_or(ptr::null_mut());
// CAST: `drm_gpuvm_bo` is first field of `Self`.
raw_ptr.cast()
}
/// Custom function for freeing a `drm_gpuvm_bo`.
///
/// # Safety
///
/// The pointer must have been allocated with [`GpuVmBo::ALLOC_FN`], and must not be used after
/// this call.
unsafe extern "C" fn vm_bo_free(ptr: *mut bindings::drm_gpuvm_bo) {
// CAST: `drm_gpuvm_bo` is first field of `Self`.
// SAFETY:
// * The ptr was allocated from kmalloc with the layout of `GpuVmBo<T>`.
// * `ptr->inner` has no destructor.
// * `ptr->data` contains a valid `T::VmBoData` that we can drop.
drop(unsafe { KBox::<Self>::from_raw(ptr.cast()) });
}
/// Access this [`GpuVmBo`] from a raw pointer.
///
/// # Safety
///
/// For the duration of `'a`, the pointer must reference a valid `drm_gpuvm_bo` associated with
/// a [`GpuVm<T>`]. The BO must also be present in the GEM list.
#[inline]
pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm_bo) -> &'a Self {
// SAFETY: `drm_gpuvm_bo` is first field and `repr(C)`.
unsafe { &*ptr.cast() }
}
/// Returns a raw pointer to underlying C value.
#[inline]
pub fn as_raw(&self) -> *mut bindings::drm_gpuvm_bo {
self.inner.get()
}
/// The [`GpuVm`] that this GEM object is mapped in.
#[inline]
pub fn gpuvm(&self) -> &GpuVm<T> {
// SAFETY: The `obj` pointer is guaranteed to be valid.
unsafe { GpuVm::<T>::from_raw((*self.inner.get()).vm) }
}
/// The [`drm_gem_object`](DriverGpuVm::Object) for these mappings.
#[inline]
pub fn obj(&self) -> &T::Object {
// SAFETY: The `obj` pointer is guaranteed to be valid.
unsafe { <T::Object as IntoGEMObject>::from_raw((*self.inner.get()).obj) }
}
/// The driver data with this buffer object.
#[inline]
pub fn data(&self) -> &T::VmBoData {
&self.data
}
pub(super) fn lock_gpuva(&self) -> crate::sync::MutexGuard<'_, ()> {
// SAFETY: The GEM object is valid.
let ptr = unsafe { &raw mut (*self.obj().as_raw()).gpuva.lock };
// SAFETY: The GEM object is valid, so the mutex is properly initialized.
let mutex = unsafe { crate::sync::Mutex::from_raw(ptr) };
mutex.lock()
}
}
/// A pre-allocated [`GpuVmBo`] object.
///
/// # Invariants
///
/// Points at a `drm_gpuvm_bo` that contains a valid `T::VmBoData`, has a refcount of one, and is
/// absent from any gem, extobj, or evict lists.
pub(super) struct GpuVmBoAlloc<T: DriverGpuVm>(NonNull<GpuVmBo<T>>);
impl<T: DriverGpuVm> GpuVmBoAlloc<T> {
/// Create a new pre-allocated [`GpuVmBo`].
///
/// It's intentional that the initializer is infallible because `drm_gpuvm_bo_put` will call
/// drop on the data, so we don't have a way to free it when the data is missing.
#[inline]
pub(super) fn new(
gpuvm: &GpuVm<T>,
gem: &T::Object,
value: impl PinInit<T::VmBoData>,
) -> Result<GpuVmBoAlloc<T>, AllocError> {
// CAST: `GpuVmBoAlloc::vm_bo_alloc` ensures that this memory was allocated with the layout
// of `GpuVmBo<T>`. The type is repr(C), so `container_of` is not required.
// SAFETY: The provided gpuvm and gem ptrs are valid for the duration of this call.
let raw_ptr = unsafe {
bindings::drm_gpuvm_bo_create(gpuvm.as_raw(), gem.as_raw()).cast::<GpuVmBo<T>>()
};
let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?;
// SAFETY: `ptr->data` is a valid pinned location.
unsafe { pin_init::raw_init(&raw mut (*raw_ptr).data, value) };
// INVARIANTS: We just created the vm_bo so it's absent from lists, and the data is valid
// as we just initialized it.
Ok(GpuVmBoAlloc(ptr))
}
/// Returns a raw pointer to underlying C value.
#[inline]
pub(super) fn as_raw(&self) -> *mut bindings::drm_gpuvm_bo {
// SAFETY: The pointer references a valid `drm_gpuvm_bo`.
unsafe { (*self.0.as_ptr()).inner.get() }
}
/// Look up whether there is an existing [`GpuVmBo`] for this gem object.
///
/// The caller should not hold the GEM mutex or DMA resv lock.
#[inline]
pub(super) fn obtain(self) -> ARef<GpuVmBo<T>> {
let me = ManuallyDrop::new(self);
// SAFETY: Valid `drm_gpuvm_bo` not already in the lists. We do not access `me` after this
// call.
let ptr = unsafe { bindings::drm_gpuvm_bo_obtain_prealloc(me.as_raw()) };
// SAFETY: `drm_gpuvm_bo_obtain_prealloc` always returns a non-null ptr
let nonnull = unsafe { NonNull::new_unchecked(ptr.cast()) };
// INVARIANTS: `drm_gpuvm_bo_obtain_prealloc` ensures that the bo is in the GEM list.
// SAFETY: We received one refcount from `drm_gpuvm_bo_obtain_prealloc`.
let ret = unsafe { ARef::<GpuVmBo<T>>::from_raw(nonnull) };
// Ensure that external objects are in the extobj list.
//
// Note that we must call `extobj_add` even if `ptr != me` to avoid a race condition where
// we could end up using the extobj before the thread with `ptr == me` calls extobj_add.
if ret.gpuvm().is_extobj(ret.obj()) {
let resv_lock = ret.gpuvm().raw_resv();
// TODO: Use a proper lock guard here once a dma_resv lock abstraction exists.
// SAFETY: The GPUVM is still alive, so its resv lock is too.
unsafe { bindings::dma_resv_lock(resv_lock, ptr::null_mut()) };
// SAFETY: We hold the GPUVMs resv lock.
unsafe { bindings::drm_gpuvm_bo_extobj_add(ptr) };
// SAFETY: We took the lock, so we can unlock it.
unsafe { bindings::dma_resv_unlock(resv_lock) };
}
ret
}
}
impl<T: DriverGpuVm> Deref for GpuVmBoAlloc<T> {
type Target = GpuVmBo<T>;
#[inline]
fn deref(&self) -> &GpuVmBo<T> {
// SAFETY: By the type invariants we may deref while `Self` exists.
unsafe { self.0.as_ref() }
}
}
impl<T: DriverGpuVm> Drop for GpuVmBoAlloc<T> {
#[inline]
fn drop(&mut self) {
// TODO: Call drm_gpuvm_bo_destroy_not_in_lists() directly.
// SAFETY: It's safe to perform a deferred put in any context.
unsafe { bindings::drm_gpuvm_bo_put_deferred(self.as_raw()) };
}
}