mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-06-21 15:43:21 +02:00
37f748ed0c
Since Rust uses GPUVM via the kernel crate, which is built-in, the GPUVM module must also be built-in to use GPUVM from Rust. Adjust the Kconfig settings accordingly. Suggested-by: Danilo Krummrich <dakr@kernel.org> Signed-off-by: Alice Ryhl <aliceryhl@google.com> Link: https://patch.msgid.link/20260427-gpuvm-config-v1-1-8ece03771f8a@google.com Signed-off-by: Danilo Krummrich <dakr@kernel.org>
329 lines
10 KiB
Rust
329 lines
10 KiB
Rust
// SPDX-License-Identifier: GPL-2.0 OR MIT
|
|
|
|
#![cfg(CONFIG_RUST_DRM_GPUVM)]
|
|
|
|
//! DRM GPUVM in immediate mode
|
|
//!
|
|
//! Rust abstractions for using GPUVM in immediate mode. This is when the GPUVM state is updated
|
|
//! during `run_job()`, i.e., in the DMA fence signalling critical path, to ensure that the GPUVM
|
|
//! and the GPU's virtual address space has the same state at all times.
|
|
//!
|
|
//! C header: [`include/drm/drm_gpuvm.h`](srctree/include/drm/drm_gpuvm.h)
|
|
|
|
use kernel::{
|
|
alloc::{
|
|
AllocError,
|
|
Flags as AllocFlags, //
|
|
},
|
|
bindings,
|
|
drm,
|
|
drm::gem::IntoGEMObject,
|
|
error::to_result,
|
|
prelude::*,
|
|
sync::aref::{
|
|
ARef,
|
|
AlwaysRefCounted, //
|
|
},
|
|
types::Opaque, //
|
|
};
|
|
|
|
use core::{
|
|
cell::UnsafeCell,
|
|
marker::PhantomData,
|
|
mem::{
|
|
ManuallyDrop,
|
|
MaybeUninit, //
|
|
},
|
|
ops::{
|
|
Deref,
|
|
DerefMut,
|
|
Range, //
|
|
},
|
|
ptr::{
|
|
self,
|
|
NonNull, //
|
|
}, //
|
|
};
|
|
|
|
mod sm_ops;
|
|
pub use self::sm_ops::*;
|
|
|
|
mod vm_bo;
|
|
pub use self::vm_bo::*;
|
|
|
|
mod va;
|
|
pub use self::va::*;
|
|
|
|
/// A DRM GPU VA manager.
|
|
///
|
|
/// This object is refcounted, but the locations of mapped ranges may only be accessed or changed
|
|
/// via the special unique handle [`UniqueRefGpuVm`].
|
|
///
|
|
/// # Invariants
|
|
///
|
|
/// * Stored in an allocation managed by the refcount in `self.vm`.
|
|
/// * Access to `data` and the gpuvm interval tree is controlled via the [`UniqueRefGpuVm`] type.
|
|
/// * Does not contain any sparse [`GpuVa<T>`] instances.
|
|
#[pin_data]
|
|
pub struct GpuVm<T: DriverGpuVm> {
|
|
#[pin]
|
|
vm: Opaque<bindings::drm_gpuvm>,
|
|
/// Accessed only through the [`UniqueRefGpuVm`] reference.
|
|
data: UnsafeCell<T>,
|
|
}
|
|
|
|
// SAFETY: The GPUVM api does not assume that it is tied to a specific thread. The destructor will
|
|
// drop the `data` field, which is okay because it is guaranteed `Send` by the `DriverGpuVm` trait.
|
|
unsafe impl<T: DriverGpuVm> Send for GpuVm<T> {}
|
|
// SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel.
|
|
unsafe impl<T: DriverGpuVm> Sync for GpuVm<T> {}
|
|
|
|
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
|
|
unsafe impl<T: DriverGpuVm> AlwaysRefCounted for GpuVm<T> {
|
|
fn inc_ref(&self) {
|
|
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
|
|
unsafe { bindings::drm_gpuvm_get(self.vm.get()) };
|
|
}
|
|
|
|
unsafe fn dec_ref(obj: NonNull<Self>) {
|
|
// SAFETY: By type invariants, the allocation is managed by the refcount in `self.vm`.
|
|
unsafe { bindings::drm_gpuvm_put((*obj.as_ptr()).vm.get()) };
|
|
}
|
|
}
|
|
|
|
impl<T: DriverGpuVm> PartialEq for GpuVm<T> {
|
|
#[inline]
|
|
fn eq(&self, other: &Self) -> bool {
|
|
core::ptr::eq(self.as_raw(), other.as_raw())
|
|
}
|
|
}
|
|
impl<T: DriverGpuVm> Eq for GpuVm<T> {}
|
|
|
|
impl<T: DriverGpuVm> GpuVm<T> {
|
|
const fn vtable() -> &'static bindings::drm_gpuvm_ops {
|
|
&bindings::drm_gpuvm_ops {
|
|
vm_free: Some(Self::vm_free),
|
|
op_alloc: None,
|
|
op_free: None,
|
|
vm_bo_alloc: GpuVmBo::<T>::ALLOC_FN,
|
|
vm_bo_free: GpuVmBo::<T>::FREE_FN,
|
|
vm_bo_validate: None,
|
|
sm_step_map: Some(Self::sm_step_map),
|
|
sm_step_unmap: Some(Self::sm_step_unmap),
|
|
sm_step_remap: Some(Self::sm_step_remap),
|
|
}
|
|
}
|
|
|
|
/// Creates a GPUVM instance.
|
|
#[expect(clippy::new_ret_no_self)]
|
|
pub fn new<E>(
|
|
name: &'static CStr,
|
|
dev: &drm::Device<T::Driver>,
|
|
r_obj: &T::Object,
|
|
range: Range<u64>,
|
|
reserve_range: Range<u64>,
|
|
data: T,
|
|
) -> Result<UniqueRefGpuVm<T>, E>
|
|
where
|
|
E: From<AllocError>,
|
|
E: From<core::convert::Infallible>,
|
|
{
|
|
let obj = KBox::try_pin_init::<E>(
|
|
try_pin_init!(Self {
|
|
data: UnsafeCell::new(data),
|
|
vm <- Opaque::ffi_init(|vm| {
|
|
// SAFETY: These arguments are valid. `vm` is valid until refcount drops to
|
|
// zero. The `vm` is zeroed before calling this method by `__GFP_ZERO` flag
|
|
// below.
|
|
unsafe {
|
|
bindings::drm_gpuvm_init(
|
|
vm,
|
|
name.as_char_ptr(),
|
|
bindings::drm_gpuvm_flags_DRM_GPUVM_IMMEDIATE_MODE
|
|
| bindings::drm_gpuvm_flags_DRM_GPUVM_RESV_PROTECTED,
|
|
dev.as_raw(),
|
|
r_obj.as_raw(),
|
|
range.start,
|
|
range.end - range.start,
|
|
reserve_range.start,
|
|
reserve_range.end - reserve_range.start,
|
|
const { Self::vtable() },
|
|
)
|
|
}
|
|
}),
|
|
}? E),
|
|
GFP_KERNEL | __GFP_ZERO,
|
|
)?;
|
|
// SAFETY: This transfers the initial refcount to the ARef.
|
|
let aref = unsafe {
|
|
ARef::from_raw(NonNull::new_unchecked(KBox::into_raw(
|
|
Pin::into_inner_unchecked(obj),
|
|
)))
|
|
};
|
|
// INVARIANT: This reference is unique.
|
|
Ok(UniqueRefGpuVm(aref))
|
|
}
|
|
|
|
/// Access this [`GpuVm`] from a raw pointer.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// The pointer must reference the `struct drm_gpuvm` in a valid [`GpuVm<T>`] that remains
|
|
/// valid for at least `'a`.
|
|
#[inline]
|
|
pub unsafe fn from_raw<'a>(ptr: *mut bindings::drm_gpuvm) -> &'a Self {
|
|
// SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`. Caller ensures the
|
|
// pointer is valid for 'a.
|
|
unsafe { &*kernel::container_of!(Opaque::cast_from(ptr), Self, vm) }
|
|
}
|
|
|
|
/// Returns a raw pointer to the embedded `struct drm_gpuvm`.
|
|
#[inline]
|
|
pub fn as_raw(&self) -> *mut bindings::drm_gpuvm {
|
|
self.vm.get()
|
|
}
|
|
|
|
/// The start of the VA space.
|
|
#[inline]
|
|
pub fn va_start(&self) -> u64 {
|
|
// SAFETY: The `mm_start` field is immutable.
|
|
unsafe { (*self.as_raw()).mm_start }
|
|
}
|
|
|
|
/// The length of the GPU's virtual address space.
|
|
#[inline]
|
|
pub fn va_length(&self) -> u64 {
|
|
// SAFETY: The `mm_range` field is immutable.
|
|
unsafe { (*self.as_raw()).mm_range }
|
|
}
|
|
|
|
/// Returns the range of the GPU virtual address space.
|
|
#[inline]
|
|
pub fn va_range(&self) -> Range<u64> {
|
|
let start = self.va_start();
|
|
// OVERFLOW: This reconstructs the Range<u64> passed to the constructor, so it won't fail.
|
|
let end = start + self.va_length();
|
|
Range { start, end }
|
|
}
|
|
|
|
/// Get or create the [`GpuVmBo`] for this gem object.
|
|
#[inline]
|
|
pub fn obtain(
|
|
&self,
|
|
obj: &T::Object,
|
|
data: impl PinInit<T::VmBoData>,
|
|
) -> Result<ARef<GpuVmBo<T>>, AllocError> {
|
|
Ok(GpuVmBoAlloc::new(self, obj, data)?.obtain())
|
|
}
|
|
|
|
/// Clean up buffer objects that are no longer used.
|
|
#[inline]
|
|
pub fn deferred_cleanup(&self) {
|
|
// SAFETY: This GPUVM uses immediate mode.
|
|
unsafe { bindings::drm_gpuvm_bo_deferred_cleanup(self.as_raw()) }
|
|
}
|
|
|
|
/// Check if this GEM object is an external object for this GPUVM.
|
|
#[inline]
|
|
pub fn is_extobj(&self, obj: &T::Object) -> bool {
|
|
// SAFETY: We may call this with any GPUVM and GEM object.
|
|
unsafe { bindings::drm_gpuvm_is_extobj(self.as_raw(), obj.as_raw()) }
|
|
}
|
|
|
|
/// Free this GPUVM.
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Called when refcount hits zero.
|
|
unsafe extern "C" fn vm_free(me: *mut bindings::drm_gpuvm) {
|
|
// SAFETY: Caller passes a pointer to the `drm_gpuvm` in a `GpuVm<T>`.
|
|
let me = unsafe { kernel::container_of!(Opaque::cast_from(me), Self, vm).cast_mut() };
|
|
// SAFETY: By type invariants we can free it when refcount hits zero.
|
|
drop(unsafe { KBox::from_raw(me) })
|
|
}
|
|
|
|
#[inline]
|
|
fn raw_resv(&self) -> *mut bindings::dma_resv {
|
|
// SAFETY: `r_obj` is immutable and valid for duration of GPUVM.
|
|
unsafe { (*(*self.as_raw()).r_obj).resv }
|
|
}
|
|
}
|
|
|
|
/// The manager for a GPUVM.
|
|
pub trait DriverGpuVm: Sized + Send {
|
|
/// Parent `Driver` for this object.
|
|
type Driver: drm::Driver<Object = Self::Object>;
|
|
|
|
/// The kind of GEM object stored in this GPUVM.
|
|
type Object: IntoGEMObject;
|
|
|
|
/// Data stored with each [`struct drm_gpuva`](struct@GpuVa).
|
|
type VaData;
|
|
|
|
/// Data stored with each [`struct drm_gpuvm_bo`](struct@GpuVmBo).
|
|
type VmBoData;
|
|
|
|
/// The private data passed to callbacks.
|
|
type SmContext<'ctx>;
|
|
|
|
/// Indicates that a new mapping should be created.
|
|
fn sm_step_map<'op, 'ctx>(
|
|
&mut self,
|
|
op: OpMap<'op, Self>,
|
|
context: &mut Self::SmContext<'ctx>,
|
|
) -> Result<OpMapped<'op, Self>, Error>;
|
|
|
|
/// Indicates that an existing mapping should be removed.
|
|
fn sm_step_unmap<'op, 'ctx>(
|
|
&mut self,
|
|
op: OpUnmap<'op, Self>,
|
|
context: &mut Self::SmContext<'ctx>,
|
|
) -> Result<OpUnmapped<'op, Self>, Error>;
|
|
|
|
/// Indicates that an existing mapping should be split up.
|
|
fn sm_step_remap<'op, 'ctx>(
|
|
&mut self,
|
|
op: OpRemap<'op, Self>,
|
|
context: &mut Self::SmContext<'ctx>,
|
|
) -> Result<OpRemapped<'op, Self>, Error>;
|
|
}
|
|
|
|
/// The core of the DRM GPU VA manager.
|
|
///
|
|
/// This object is a unique reference to the VM that can access the interval tree and the Rust
|
|
/// `data` field.
|
|
///
|
|
/// # Invariants
|
|
///
|
|
/// Each `GpuVm` instance has at most one `UniqueRefGpuVm` reference.
|
|
pub struct UniqueRefGpuVm<T: DriverGpuVm>(ARef<GpuVm<T>>);
|
|
|
|
// SAFETY: The GPUVM api is designed to allow &self methods to be called in parallel, and
|
|
// concurrent access to `data` is safe due to the `T: Sync` requirement.
|
|
unsafe impl<T: DriverGpuVm + Sync> Sync for UniqueRefGpuVm<T> {}
|
|
|
|
impl<T: DriverGpuVm> UniqueRefGpuVm<T> {
|
|
/// Access the data owned by this `UniqueRefGpuVm` immutably.
|
|
#[inline]
|
|
pub fn data_ref(&self) -> &T {
|
|
// SAFETY: By the type invariants we may access `data`.
|
|
unsafe { &*self.0.data.get() }
|
|
}
|
|
|
|
/// Access the data owned by this `UniqueRefGpuVm` mutably.
|
|
#[inline]
|
|
pub fn data(&mut self) -> &mut T {
|
|
// SAFETY: By the type invariants we may access `data`.
|
|
unsafe { &mut *self.0.data.get() }
|
|
}
|
|
}
|
|
|
|
impl<T: DriverGpuVm> Deref for UniqueRefGpuVm<T> {
|
|
type Target = GpuVm<T>;
|
|
|
|
#[inline]
|
|
fn deref(&self) -> &GpuVm<T> {
|
|
&self.0
|
|
}
|
|
}
|